@Blog
過去の蓄積を見るために書く日記.

カラー選択からライトモードとダークモードに切り替えた話
2025.09.22
おはようございます.カラー選択からライトモードとダークモードに切り替えた話を書いています.カラー選択、実は結構自分は気に入っていたのだけどあまり使用されないだろうなぁという思いも前から思っていたのだけど、そのままにしていました.
切り替えたのはある動画を視聴して、その中でライトモード、ダークモードの仕様に全てのブラウザが対応したことを語っていたので、だったらこの際ライトモードダークモードに変更しようと思い立ちました.
休日の終わりごろに思い立って、サービスとギャラリー以外はライトモードダークモードに切り替えが終わりました.ただ、一つ課題があってOS(オペレーションシステム)とブラウザのモード設定がチグハグだった場合、コメント欄などが見にくくなっています.
ここだけは今のところ保留にしています.
残りのサービスとギャラリーは近日中に変更しようと思っています.
一応、切り替えを行うソースコードを貼っときますね.
document.addEventListener('DOMContentLoaded', () => {
const switchToggle = document.getElementById('flexSwitchCheckDefault');
const htmlElement = document.documentElement;
// ページの初期ロード時にテーマを設定
function setInitialTheme() {
// ローカルストレージに設定があれば、それを使う
const savedTheme = localStorage.getItem('theme');
if (savedTheme) {
htmlElement.setAttribute('data-theme', savedTheme);
switchToggle.checked = savedTheme === 'dark';
} else {
// なければOSの設定を使う
const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
htmlElement.setAttribute('data-theme', isDarkMode ? 'dark' : 'light');
switchToggle.checked = isDarkMode;
}
}
// スイッチが変更されたらテーマを切り替え、ローカルストレージに保存
switchToggle.addEventListener('change', (event) => {
const newTheme = event.target.checked ? 'dark' : 'light';
htmlElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
});
// 初期テーマを設定
setInitialTheme();
});
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, オペレーション, カラー, ギャラリー, コード, ここ, こと, コメント, ごろ, サービス, システム, スイッチ, ストレージ, ソース, それ, ダーク, チグハグ, テーマ, ところ, ブラウザ, ページ, モード, ライド, ライトモードダークモード, ローカル, ロード, 一つ, 仕様, 以外, 休日, 使用, 保存, 保留, 全て, 切り替え, 初期, 動画, 場合, 変更, 対応, 思い, 明日, 残り, 自分, 視聴, 設定, 課題, 近日, 選択,

HTMLをMarkdownに変換する方法、PHP言語編
2025.09.07
おはようございます.HTMLをMarkdownに変換する方法、PHP言語編を書いています.これでワードプレスのLLMO対策しています.まず、自分はめんどくさがり屋なので、一からHTML(ハイパーテキストマークアップランゲージ)をマークダウン記法に変えるアルゴリズムを作るのは面倒なので今回は先人が作ったライブラリーを使用します.
composer require league/html-to-markdown
因みにこのライブラリーで変換出来ない、YOUTUBEの埋め込みなどは正規表現で置き換えています.
// カスタム:YouTube埋め込み対応
$content = preg_replace_callback(
'#<iframe[^>]+src="https?://www\.youtube\.com/embed/([^"?]+)[^"]*"[^>]*></iframe>#i',
function ($matches) {
$id = $matches[1];
return "\n[](https://www.youtube.com/watch?v={$id})\n";
},
$content
);
このライブラリーの使い方は、こちらのリードミーをご確認ください.人様のコードなのでいつサポートが切れるか分からないですが、当分の間は大丈夫そうな気がしています.もしサポートが非対応になったらフォークしてカスタマイズする方法も残されていますので、あまり気にしていませんが・・・.
一番怖いのはAPIの突然のサービス停止です、これが一番厄介だと思うのでこの頃はAPIサービスは極力使用しないように頑張っていますとは言ってもSNS系はAPIサービスに頼るしかないのが現状ですね.
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, アルゴリズム, カスタマイズ, カスタム, コード, こちら, これ, サービス, サポート, そう, ダウン, ハイパーテキストマークアップランゲージ, フォーク, プレス, マーク, よう, ライブラリー, リードミー, ワード, 一番, 人様, 今回, 使い方, 使用, 停止, 先人, 厄介, 変換, 大丈夫, 対応, 対策, 当分, 方法, 明日, 正規, 現状, 確認, 自分, 表現, 言語, 記法, 面倒,

LLMO対策の前手順として、マークダウン記法を施しました.
2025.09.02
おはようございます.LLMO対策の前手順として、マークダウン記法を施しました.シングルページにマークダウン記法のリンクがそれぞれの記事にあると思います.
そのリンクをクリックするとマークダウン記法が表示されます.LLMO設置後、LLMs.txtを設置すれば良いらしいので、そちらも近日中に対応する予定です.尚、この記事がUPされている頃にはLLMs.txtの設置が終わっている気もします.
追伸:LLMs.txtの設置が終わりました、ルートに設置しています.LLMs.txtはマークダウン記法が記載されているURL(リンク)を一行ずつ表示するような形式で良いらしいです.
これをチャッピーさんにプラグインにして貰いましたのでお裾分けです.下記のプラグインを導入すると全記事URL(パラメーター(マークダウン記法と認識させる))が発行されます.
※マークダウン記法したページはご自身で作る必要があります.
<?php
/*
Plugin Name: LLMS URL Exporter (Fixed)
Description: 公開記事のURLをuploads/llms.txt に日付降順で出力します。管理画面のボタンで非同期(AJAX)実行。新規公開時は先頭に追記。
Version: 1.1
Author: taoka toshiaki
*/
if ( ! defined( 'ABSPATH' ) ) exit;
class LLMS_URL_Exporter {
private $file;
public function __construct() {
$this->file = $_SERVER['DOCUMENT_ROOT'] . '/llms.txt';
add_action( 'admin_menu', [ $this, 'add_admin_page' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_admin_script' ] );
// AJAX (管理画面からの呼び出し = ログインユーザー用)
add_action( 'wp_ajax_llms_generate_urls', [ $this, 'generate_urls' ] );
// 投稿が公開になったとき(状態遷移を監視)
add_action( 'transition_post_status', [ $this, 'on_transition_post_status' ], 10, 3 );
register_activation_hook( __FILE__, [ $this, 'create_file_on_activate' ] );
}
public function create_file_on_activate() {
$dir = dirname( $this->file );
if ( ! is_dir( $dir ) ) {
wp_mkdir_p( $dir );
}
if ( ! file_exists( $this->file ) ) {
@file_put_contents( $this->file, "" );
}
}
public function add_admin_page() {
add_menu_page(
'LLMS URL Exporter',
'LLMS Export',
'manage_options',
'llms-url-exporter',
[ $this, 'admin_page_html' ]
);
}
public function enqueue_admin_script( $hook ) {
// 管理画面の当該ページだけに読み込む
if ( $hook !== 'toplevel_page_llms-url-exporter' ) return;
// jQuery は管理画面に既に存在
wp_enqueue_script( 'llms-admin', plugins_url( 'llms-admin.js', __FILE__ ), [ 'jquery' ], '1.0', true );
wp_localize_script( 'llms-admin', 'LLMS_Ajax', [
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'llms_generate_urls_nonce' ),
] );
// もし外部ファイルを用意しないなら、admin_page_html 内の inline script がフォールバックになります。
}
public function admin_page_html() {
?>
<div class="wrap">
<h1>LLMS URL Exporter</h1>
<p>公開中の全投稿URLを <code>/llms.txt</code> に日付降順で書き出します。</p>
<button id="llms-generate-btn" class="button button-primary">全記事URLを書き出す</button>
<div id="llms-result" style="margin-top:12px;"></div>
</div>
<?php
// フォールバック用のインラインスクリプト(もし外部 JS を作らない場合に有効)
$nonce = wp_create_nonce( 'llms_generate_urls_nonce' );
$ajax_url = admin_url( 'admin-ajax.php' );
?>
<script type="text/javascript">
jQuery(function($){
$('#llms-generate-btn').on('click', function(){
var $res = $('#llms-result');
$res.text('処理中...');
var data = {
action: 'llms_generate_urls',
_ajax_nonce: (typeof LLMS_Ajax !== 'undefined' ? LLMS_Ajax.nonce : '<?php echo esc_js( $nonce ); ?>')
};
var url = (typeof LLMS_Ajax !== 'undefined' ? LLMS_Ajax.ajax_url : '<?php echo esc_js( $ajax_url ); ?>');
$.post(url, data, function(response){
if ( response && response.success ) {
$res.html( response.data.message );
} else if ( response && response.data && response.data.message ) {
$res.html('エラー: ' + response.data.message);
} else {
$res.html('不明なレスポンス: ' + JSON.stringify(response));
}
}).fail(function(jqXHR, textStatus, errorThrown){
$res.html('AJAX エラー: ' + textStatus + ' ' + errorThrown + ' (status: ' + jqXHR.status + ')');
});
});
});
</script>
<?php
}
// AJAX ハンドラ
public function generate_urls() {
// nonce と権限チェック
check_ajax_referer( 'llms_generate_urls_nonce', '_ajax_nonce' );
if ( ! current_user_can( 'manage_options' ) ) {
wp_send_json_error( [ 'message' => '権限がありません。' ], 403 );
}
$args = [
'post_type' => 'post',
'post_status' => 'publish',
'orderby' => 'date',
'order' => 'DESC',
'posts_per_page' => -1,
'no_found_rows' => true,
'fields' => 'ids',
];
$posts = get_posts( $args );
$urls = [];
foreach ( $posts as $pid ) {
$permalink = get_permalink( $pid );
if ( $permalink ) $urls[] = $permalink.'?md=1';
}
$content = implode("\n", $urls);
$written = @file_put_contents( $this->file, $content );
if ( $written === false ) {
$err = error_get_last();
$msg = isset( $err['message'] ) ? $err['message'] : '不明な書き込みエラー';
wp_send_json_error( [ 'message' => 'ファイル書き込みに失敗しました: ' . $msg ] );
}
wp_send_json_success( [ 'message' => 'llms.txt に ' . count( $urls ) . ' 件のURLを書き出しました。' ] );
}
// 公開判定:状態遷移で publish になったときに先頭へ追加
public function on_transition_post_status( $new_status, $old_status, $post ) {
if ( $post->post_type !== 'post' ) return;
if ( $new_status === 'publish' && $old_status !== 'publish' ) {
$url = get_permalink( $post->ID ).'?md=1';
$current = '';
if ( file_exists( $this->file ) ) {
$current = file_get_contents( $this->file );
}
// 既存の同一URLがあれば削除して先頭へ
$lines = array_filter( array_map( 'trim', explode("\n", $current ) ) );
$lines = array_values( array_diff( $lines, [ $url ] ) );
array_unshift( $lines, $url );
$new_content = implode("\n", $lines);
@file_put_contents( $this->file, $new_content );
}
}
}
new LLMS_URL_Exporter();
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, いん, インライン, エラー, お裾分け, クリック, これ, サン, シングル, スクリプト, そちら, それぞれ, ダウン, チェック, チャッピー, とき, パラメーター, ハンドラ, ファイル, フォールバック, フラグ, ページ, ボタン, マーク, ユーザー, よう, リンク, ルート, レスポンス, ログイン, 一行, 下記, 不明, 予定, 先頭, 公開, 処理, 出力, 判定, 削除, 同一, 呼び出し, 場合, 外部, 失敗, 存在, 実行, 対応, 対策, 導入, 当該, 形式, 必要, 手順, 投稿, 新規, 既存, 日付, 明日, 書き込み, 有効, 権限, 状態, 用意, 画面, 発行, 監視, 管理, 自身, 表示, 記事, 記法, 記載, 設置, 認識, 近日, 追伸, 追加, 追記, 遷移, 降順, 非同期,

ブログをワードプレスからLaravelへ
2025.09.01
おはようございます.ブログをワードプレスからLaravelへ置き換えていこうか悩んでいます.チャッピーさんで調べった結果.
WordPress を Laravel に統合またはブログを移行する際に使える「プラグイン」やライブラリについて、おすすめの方法とその特徴を整理してご紹介します。
おすすめの主要アプローチ
1. Corcel
- WordPress データ(投稿、カスタム投稿タイプ、メタデータなど)を Laravel から直接取得できる Eloquent ベースの橋渡しライブラリです。Composer で簡単にインストールでき、設定ファイルで WP の DB 接続やモデル構成を指定可能です。(WP Engine, GitHub)
- WordPress を「バックエンド」として活用しつつ、Laravel を「フロントエンド/アプリロジック基盤」として完全に使いたい場合に最適です。
2. WordPress Pete
- WordPress 側にプラグイン(アプリのように動作)として組み込み、WordPress 管理画面から Laravel との統合を設定できます。試用も可能です。(WP Engine, Aalpha)
- Laravel 側から WordPress コンテンツを表示・活用したい場合に便利です。
3. Acorn
- Roots.io が開発したライブラリで、Laravel の Blade テンプレートや Artisan コマンド、マイグレーション機能を WordPress 内で使えるようにします。(Roots)
- WordPress 内でモダンな Laravel 開発体験(Blade, Artisan, Migration)ができるようになります。例えとして:
wp acorn make:controller
や Blade テンプレートの活用など。(Roots)
その他のアプローチ・参考情報
- WordPress REST API を使う方法
WordPress から REST API 経由でコンテンツを取得し、Laravel 側で API 呼び出しする形もあり。比較的シンプルです。(Laracasts, Codeable) - 中ミドル層としての統合
Laravel をバックエンド、WordPress をフロントエンドに設定し、ログイン機能や API 呼び出しを含めた構成も可能です。(Medium) - Reddit 上の意見
Laravel はあくまで「フレームワーク」であり、WordPress は即使える CMS。ブログ用途中心であれば WordPress のままでも十分との声もあります。但し、October CMS や Canvas のような Laravel ベース CMS を使う選択肢もあります。(Reddit)
比較まとめ
方法 | 特徴・用途 |
---|---|
Corcel | WordPress DB を Laravel で直接使う。柔軟で強力。 |
WordPress Pete | WordPress 管理画面から簡単に Laravel 連携を設定可能。 |
Acorn | WordPress 内で Laravel 開発体験(Blade, Artisan)を実現。 |
REST API | API 経由で疎結合に統合。軽量で保守性あり。 |
Laravel CMS (Canvas, October CMS) | Laravel 上に CMS 機能を構築したい場合に有効。 |
まとめ
- 即時利用・簡単統合:まずは WordPress Pete を試してみるのがおすすめ。
- 柔軟で高性能な連携 を求めるなら、Corcel による直接 DB アクセスが強み。
- 開発体験の統一(Blade や Artisan を WordPress 内で使いたい場合)は Acorn が魅力的です。
- 軽めの統合・API中心構成 を望む場合は、REST API を使ったアプローチも便利です。
という結果が返ってきたわけですが、一番妥当なのはCorcelかなと思っています、ただ、本当に移行するかは結構大変な感じがします.移植すれば後々、いろいろと出来ることが広がるかなと思っています.フロント側をvue(ビュー)かReact(リアクト)に変更するにしてもメンテは楽になりそうな予感もします.
悩ましいですね…
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, (:, アクセス, アプリ, アプリロジック, アプローチ, いん, インストール, エンド, おすすめ, カスタム, こと, コマンド, コンテンツ, サン, シンプル, そう, その他, タイプ, チャッピー, データ, テンプレート, バック, ビュー, ファイル, フラグ, フレーム, プレス, ブログ, フロント, ベース, マイグレーション, まとめ, まま, ミドル, メタデータ, メンテ, モダン, モデル, よう, ライブラリ, リアクト, ログイン, ワーク, ワード, わけ, 一番, 中心, 主要, 予感, 体験, 便利, 保守, 利用, 動作, 十分, 即時, 参考, 取得, 可能, 呼び出し, 基盤, 場合, 変更, 大変, 妥当, 完全, 実現, 強み, 強力, 後々, 情報, 意見, 感じ, 投稿, 指定, 接続, 整理, 方法, 明日, 最適, 有効, 柔軟, 構成, 構築, 橋渡し, 機能, 比較, 活用, 特徴, 用途, 画面, 直接, 移植, 移行, 管理, 簡単, 紹介, 経由, 結合, 結果, 統一, 統合, 表示, 設定, 試用, 軽量, 連携, 選択肢, 開発, 高性能, 魅力,

タグを出現回数によってタグの文字の大きさを変更するコードを実装しました.
2025.08.25
おはようございます.先日の休みに昼寝から目を覚ましたときに「あっそういや」と思って実装した機能がタグを出現回数によってタグの文字の大きさを変更するコードでした.記事の最後の最後あたりに付いているタグ文字の大きさが記事によって変わっているのが分かると思います.

こういうアイディアはもともとあるけれど、そのアイディアさえも思いつかなくなってしまったら悩ましいですね.因みにリアクトをこのサイトに実装するという計画はまだ実行に移していません.
if(document.querySelector('#main_content')){
let txt = document.querySelector('#main_content').textContent;
let tagElements = document.querySelectorAll('.tag');
[...tagElements].forEach(element => {
element.style.fontSize = fontSizeChange(txt,element.textContent);
});
}
function fontSizeChange(text,word)
{
const regex = new RegExp(word, "gi");
const matches = text.match(regex);
let cnt = matches?.length ?? 0;
const sizes = ['medium', 'x-large', 'xx-large', 'xxx-large'];
let fontSize = sizes[
cnt > 6 ? 3 :
cnt > 4 ? 2 :
cnt > 2 ? 1 : 0
];
移したら技術共有か何かで記事を書くかもしれないです.
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, アイディア, あたり, コード, サイト, タグ, とき, リアクト, 休み, 先日, 共有, 出現, 回数, 変更, 実行, 実装, 技術, 文字, 明日, 昼寝, 最後, 機能, 計画, 記事,

Scraper API(スクレイパーAPI)という物を使用すればJS駆動のサイトでもサイトを解析
2025.08.02
おはようございます.Scraper API(スクレイパーAPI)という物を使用すればJS駆動のサイトでもサイトを解析出来るようですね.殆どの近年作られたサイトはどこかしらでJavascriptのコードで動いている部分があるのでスクレイピングが出来ない.
またジャバスクリプトがオンになっていないとページ(サイト)の読み込みが出来ないサイトもあるのが現状だと思います.
そこでScraper API(スクレイパーAPI)というサービスを使用すればそういう解析が出来る.因みにVPSやAWSなどのサーバーだったらNodeJsを導入すれば簡単に解析が出来るのだけど、国内のレンタルサーバーなどでは、そもそもNodeJsがインストール出来ないのである.
スクレイパーAPIの使い方は簡単でPHP言語で使用したい場合はこんな感じです.
<?php
// APIキーと対象URL
$apiKey = 'xxxxxx';
$targetUrl = 'https://example.com?var1=value1&var2=value2';
// GET用のScraperAPIリクエストURLを構築(render=trueでJS有効)
$scraperApiUrl = 'https://api.scraperapi.com?' . http_build_query([
'api_key' => $apiKey,
'url' => $targetUrl,
'render' => 'true' // JavaScriptを有効にする
]);
// cURLでGETリクエスト送信
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $scraperApiUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
// レスポンス出力
print_r($response);
尚、こちらのコードはGETパターンですがPOSTでも操作出来たりします.SDKもあるのでそちらで対応したほうがやりやすいかもです.
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, インストール, オン, かしら, カモ, キー, コード, こちら, サーバー, サービス, サイト, ジャバスクリプト, スクレイパー, スクレイピング, そちら, そもそも, どこ, パターン, ページ, ほう, よう, リクエスト, レスポンス, レンタル, 使い方, 使用, 出力, 国内, 場合, 対応, 対象, 導入, 感じ, 操作, 明日, 有効, 構築, 殆ど, 現状, 簡単, 解析, 言語, 読み込み, 近年, 送信, 部分, 駆動,

駆け出しエンジニアとつながりたい的なコードを書いてみた.ごっさんです.
2025.07.21
おはようございます.下記のコードを知っておくと便利ですよ、フレームワークとかなら使わないかもしれないけども素(えいてぃぶコード)のPHP言語で書くと便利ですね.この頃、素のPHP言語で書くことが無くなりつつあります.仕事ではフレームワークやフロントエンドでもライブラリを使用してコードを書くことが多いですね.たまにバニラJSで書くこともありますが.
<?php
$foo = [
'name' => '山田 太郎',
'age' => 25,
'tel' => '09012341234'
];
var_dump($foo);
var_dump(array_keys($foo));
var_dump(array_values($foo));
駆け出しエンジニアがもういないよなぁ…コードの土台はAIによって書くことが多くなりプロジェクトの改修時に古のコードを見るようになるだろうなぁ.たまに見かける痛いコードを昔は書いていたなぁと思いつつ、今でも痛いコードを書いていると思った方が良いなって.数ヶ月前に書いたコードが痛いコードに見えるので…まだまだ成長過程かなって思っています.
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, イブ, エンジニア, エンド, ヶ月, コード, こと, たま, バニラ, フレーム, プロジェクト, フロント, よう, ライブラリ, ワーク, 下記, 仕事, 使用, 便利, 土台, 太郎, 山田, 成長, 改修, 明日, 言語, 過程, 駆け出し,

ワードプレスのテーブルwp_postsデータを取ってきてLaravelで表示.
2025.07.15
おはようございます.ワードプレスのテーブル(wp_posts)データを取ってきて別サイト(Laravel)にて表示してます.別サイトには広告が付かない感じです.因みにデータは自サイトのブログデータを使用していますが随時去年あたりのデータから取得するようにSQLに記述しています.
尚、どうやって取得しているかをお話するとSQLのコネクション変更する設定をコンフィグフォルダのデータベースファイルに記述してモデルにもその事を明記しています.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Blog extends Model
{
protected $connection = 'second_db';
protected $table = 'wp_posts';
}
そしてそれらを呼び出しているだけです、とても簡単なコードで動いています.そんな感じなのでブレイドのコードをシェアします、流石にコントローラーは見せられないけどYoutubeのリンクやXのリンクは出力する前に置き換えていることをヒントとしてお伝えしときますね.
<!doctype html>
<html lang="ja" class="transition-colors duration-300">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<meta name="robots" content="noindex,nofollow" />
<title>blog - [taoka toshiaki]</title>
<link rel="icon" href="https://taoka-toshiaki.com/images/g4DV7mRe_400x400.jpg?t=1752317617"
sizes="16x16 24x24 32x32 48x48 64x64">
<!-- Prism CSS -->
<link id="prism-theme" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs@1/plugins/line-numbers/prism-line-numbers.css" />
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.css"
rel="stylesheet" />
<style>
body {
background-color: #f9fafb;
color: #1f2937;
transition: background-color 0.3s, color 0.3s;
word-wrap: break-word;
overflow-wrap: break-word;
}
.dark body {
background-color: #242425;
color: #f9fafb;
}
article {
background-color: #fff;
transition: background-color 0.3s;
word-wrap: break-word;
overflow-wrap: break-word;
}
.dark article {
background-color: #3d3d47;
}
time {
color: #6b7280;
}
.dark time {
color: #9ca3af;
}
.prose {
color: inherit;
word-wrap: break-word;
overflow-wrap: break-word;
}
pre[class*="language-"] {
overflow-x: auto;
padding: 1em;
border-radius: 0.5rem;
}
code[class*="language-"] {
white-space: pre;
}
a,
a:hover {
border-bottom: solid 2px #fbf81c;
}
</style>
@include('header_script')
</head>
<body class="transition-colors duration-300">
<div class="max-w-3xl mx-auto p-6">
<h1 class="text-2xl font-bold mb-6"><a href="/" class="text-current">🏠️Top Page</a></h1>
<p class="mb-3 font-bold"><a href="/blog">🖊️</a>おもしろき こともなき世をおもしろく 住みなすものは 心なりけり.</p>
@if (request()->input('page') == 108)
<a href="/blog" class="text-[54px]">📖</a>
<h1 class="text-2xl font-bold text-[108px] mb-6">108🧘️</h1>
@endif
<button id="darkModeToggle"
class="mb-6 px-4 py-2 bg-gray-200 dark:bg-gray-700 text-gray-800 dark:text-gray-200 rounded">
ダークモード切り替え
</button>
<div class="space-y-10">
@foreach ($items as $item)
<article class="shadow-md rounded-lg p-6 transition-colors duration-300">
<h2 class="text-xl font-semibold mb-2 break-words">⏩️{{ $item->post_title }}</h2>
<time class="text-sm block mb-4">{{ $item->post_date }}</time>
<div class="prose prose-sm max-w-none break-words">
{!! $item->post_content !!}
</div>
</article>
@endforeach
</div>
<div class="mt-10">
{{ $items->links() }}
</div>
</div>
<!-- トップに戻るボタン -->
<button id="scrollTopBtn"
class="fixed bottom-6 right-6 hidden bg-gray-600 text-white px-4 py-2 rounded-full shadow-lg hover:bg-gray-700 transition-opacity">
▲ 上へ戻る
</button>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<script src="https://cdn.jsdelivr.net/clipboard.js/1.5.13/clipboard.min.js"></script>
<!-- Prism.jsのコアファイル -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-core.min.js"></script>
<!-- Prism.jsのオートローダ -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
<!-- Toolbarプラグインのスクリプト -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/toolbar/prism-toolbar.min.js"></script>
<!-- その他のプラグインのスクリプト -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/plugins/highlight-keywords/prism-highlight-keywords.min.js"></script>
<script>
const toggleBtn = document.getElementById('darkModeToggle');
const prismTheme = document.getElementById('prism-theme');
const darkThemeHref = 'https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism-tomorrow.css';
const lightThemeHref = 'https://cdn.jsdelivr.net/npm/prismjs@1/themes/prism.css';
if (localStorage.getItem('darkMode') === 'true') {
document.documentElement.classList.add('dark');
prismTheme.href = darkThemeHref;
}
toggleBtn.addEventListener('click', () => {
const isDark = document.documentElement.classList.toggle('dark');
prismTheme.href = isDark ? darkThemeHref : lightThemeHref;
localStorage.setItem('darkMode', isDark);
});
document.addEventListener("DOMContentLoaded", () => {
document.querySelectorAll('pre[class*="lang-"]').forEach(pre => {
const match = pre.className.match(/lang-(\w+)/);
if (match) {
const lang = match[1];
pre.className = `language-${lang} line-numbers`;
const code = pre.querySelector('code');
if (code) code.className = `language-${lang}`;
}
});
// トップへ戻るボタンの処理
const scrollTopBtn = document.getElementById("scrollTopBtn");
window.addEventListener("scroll", () => {
scrollTopBtn.classList.toggle("hidden", window.scrollY < 200);
});
scrollTopBtn.addEventListener("click", () => {
window.scrollTo({
top: 0,
behavior: "smooth"
});
});
});
//全選択ボタンを作成&クラス名付与
Prism.plugins.toolbar.registerButton('select-code', function(env) {
var newButton = document.createElement('button');
newButton.className = 'code-select-btn';
newButton.innerHTML = 'コードを選択';
newButton.addEventListener('click', function() {
if (document.body.createTextRange) { // ms
var range = document.body.createTextRange();
range.moveToElementText(env.element);
range.select();
} else if (window.getSelection) { // moz, opera, webkit
var selection = window.getSelection();
var range = document.createRange();
range.selectNodeContents(env.element);
selection.removeAllRanges();
selection.addRange(range);
}
});
return newButton;
});
//使用言語表示ボタンを作成&クラス名付与
Prism.plugins.toolbar.registerButton('show-lang-btn', function(env) {
var newButton2 = document.createElement('button');
newButton2.className = 'show-lang-btn';
newButton2.innerHTML = env.language;
return newButton2;
});
$(function() {
//各ボタン要素の親要素にクラス名付与
$('.toolbar-item .code-select-btn').parents('.toolbar-item').addClass('code-select');
});
</script>
</body>
</html>
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, ;;⏩, あたり, いん, オート, お伝え, お話, クラス, コード, コア, こと, コネクション, コントローラー, コンフィグフォルダ, サイト, シェア, スクリプト, その他, それら, ダーク, データ, データベース, テーブル, ドップ, ヒント, ファイル, フラグ, ブレイド, プレス, ブログデータ, ボタン, モード, モデル, もの, よう, リンク, ローダ, ワード, 付与, 作成, 使用, 処理, 出力, 切り替え, 去年, 取得, 変更, 広告, 感じ, 明日, 明記, 流石, 簡単, 表示, 要素, 言語, 記述, 設定, 選択, 随時,

Jsonの返却時に便利ですよを貼っときますねと雑煮.
2025.07.14
おはようございます.Jsonの返却時に便利ですよを貼っときますね、こう書くのがベストプラクティスなのか分かりませんが便利な時がありますので覚えておいて損はない書き方だと思います.
//Jsonの返却時に便利ですよ👍️
const obj = {
name: "Taoka",
age: 25,
country: "Japan"
};
Object.entries(obj).forEach(([key, value]) => {
console.log(key,value)
});
//console log ⬇️
//name Taro
//age 25
//country Japan
雑煮ではなくて雑談です、最近?のUPDATEでウィンドウズ11のメモ帳でもマークダウン記法が使えるようになったソウデス.ちょっと驚きました、なんかメモ帳ってウィルスに感染しないぐらい強固なアプリなイメージがあるので、いまのメモ帳は今まで無かったものを拡張していっている感じがしてちょっと心配なイメージが少しあります.
便利になることは良いことですが、拡張していくとバグや脆弱性が発生するので大丈夫かなって思います.マイクロソフト開発陣営は優秀な人だと思いますが、やっぱ心配.
全然関係ない話をもう一つウィンドウズをアップデートしていく内に何だか家のパソコンが熱暴走で…固まりだした.この原因を探ろうとログを見て解決したいと思っています.原因が分かったら記事にしようと思います.
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, アップデート, アプリ, いま, イメージ, ウィルス, ウィンドウズ, こと, そうです, ダウン, なん, バグ, パソコン, ベストプラクティス, マーク, マイクロソフト, メモ, もの, よう, ログ, 一つ, 便利, 優秀, 原因, 大丈夫, 強固, 心配, 感じ, 感染, 拡張, 明日, 暴走, 書き方, 最近, 発生, 脆弱, 解決, 記事, 記法, 返却, 開発, 関係, 陣営, 雑煮, 雑談,

チャットワークのAPIを使ってみました.プロンプトでほぼ書いています.
2025.07.13
おはようございます.チャットワークのAPIを使ってみました.プロンプトでほぼ書いたコードになります、チャットGPTの無料版にリファレンスのURLリンクとPHPのクラス化、リターンに$thisで返却出来る所は$thisを使用してスマートにコードを書いてと指示を出しました.
出来上がったコードが下記になります.ソースコードは自分の方でモンキーテスト的に動かしてみましたが、ちゃんと動作するようです.
<?php
class ChatworkClient
{
private string $apiToken;
private string $baseUrl = 'https://api.chatwork.com/v2';
private int $retryCount = 3;
private int $retryDelay = 1000000; // microseconds
public function __construct(string $apiToken)
{
$this->apiToken = $apiToken;
}
public function setRetry(int $count, int $delayMicroseconds): self
{
$this->retryCount = $count;
$this->retryDelay = $delayMicroseconds;
return $this;
}
private function request(string $method, string $path, array $params = []): array
{
$attempts = 0;
while ($attempts < $this->retryCount) {
$attempts++;
$ch = curl_init();
$url = $this->baseUrl . $path;
if ($method === 'GET' && $params) {
$url .= '?' . http_build_query($params);
}
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$headers = ['X-ChatWorkToken: ' . $this->apiToken];
if (in_array($method, ['POST', 'PUT', 'DELETE'])) {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
$headers[] = 'Content-Type: application/x-www-form-urlencoded';
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);
if ($error) {
if ($attempts < $this->retryCount) {
usleep($this->retryDelay);
continue;
}
throw new RuntimeException("cURL error after {$attempts} attempts: {$error}");
}
if ($status >= 200 && $status < 300) {
return [
'status' => $status,
'body' => $body ? json_decode($body, true) : null,
];
}
if ($attempts < $this->retryCount && $status >= 500) {
usleep($this->retryDelay);
continue;
}
return [
'status' => $status,
'body' => $body ? json_decode($body, true) : null,
];
}
throw new RuntimeException("Request failed after {$this->retryCount} attempts");
}
public function setToken(string $token): self
{
$this->apiToken = $token;
return $this;
}
public function me(): array
{
return $this->request('GET', '/me');
}
public function getMyStatus(): array
{
return $this->request('GET', '/my/status');
}
public function getMyTasks(array $filters = []): array
{
return $this->request('GET', '/my/tasks', $filters);
}
public function getContacts(): array
{
return $this->request('GET', '/contacts');
}
public function getRooms(): array
{
return $this->request('GET', '/rooms');
}
public function createRoom(array $params): array
{
return $this->request('POST', '/rooms', $params);
}
public function getRoom(int $roomId): array
{
return $this->request('GET', "/rooms/{$roomId}");
}
public function updateRoom(int $roomId, array $params): array
{
return $this->request('PUT', "/rooms/{$roomId}", $params);
}
public function deleteRoom(int $roomId, string $action = 'leave'): array
{
return $this->request('DELETE', "/rooms/{$roomId}", ['action_type' => $action]);
}
public function getMembers(int $roomId): array
{
return $this->request('GET', "/rooms/{$roomId}/members");
}
public function updateMembers(int $roomId, array $params): array
{
return $this->request('PUT', "/rooms/{$roomId}/members", $params);
}
public function getMessages(int $roomId, bool $force = false): array
{
return $this->request('GET', "/rooms/{$roomId}/messages", ['force' => $force ? 1 : 0]);
}
public function postMessage(int $roomId, string $body, bool $selfUnread = false): self
{
$this->request('POST', "/rooms/{$roomId}/messages", ['body' => $body, 'self_unread' => $selfUnread ? 1 : 0]);
return $this;
}
public function markRead(int $roomId): self
{
$this->request('PUT', "/rooms/{$roomId}/messages/read");
return $this;
}
public function markUnread(int $roomId): self
{
$this->request('PUT', "/rooms/{$roomId}/messages/unread");
return $this;
}
public function getMessage(int $roomId, int $messageId): array
{
return $this->request('GET', "/rooms/{$roomId}/messages/{$messageId}");
}
public function getRoomTasks(int $roomId, array $filters = []): array
{
return $this->request('GET', "/rooms/{$roomId}/tasks", $filters);
}
public function createTask(int $roomId, array $params): array
{
return $this->request('POST', "/rooms/{$roomId}/tasks", $params);
}
public function uploadFile(int $roomId, string $filePath, string $message = ''): array
{
if (!file_exists($filePath)) {
throw new InvalidArgumentException("File not found: {$filePath}");
}
$ch = curl_init();
$url = $this->baseUrl . "/rooms/{$roomId}/files";
$cfile = curl_file_create($filePath);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['X-ChatWorkToken: ' . $this->apiToken]);
curl_setopt($ch, CURLOPT_POSTFIELDS, ['file' => $cfile, 'message' => $message]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (curl_errno($ch)) {
throw new RuntimeException(curl_error($ch));
}
curl_close($ch);
return ['status' => $status, 'body' => json_decode($body, true)];
}
}
人が今回のコードを書いた場合、早い人でも10分ぐらいはコードを書かないといけないと思います、どんなに早くてもそれぐらいの時間は必要だと思いますが、生成AIはこれを数十秒で書ける訳ですから、確実に時間短縮になります.
なので人工知能が使える現場は間違いなく最初のコード出力は人工知能に任せた方が良いです.特に新規案件の土台は生成AIに任せると開発コストは削減出来ます.
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, クラス, コード, コスト, これ, スマート, ソース, それ, チャット, テスト, プロンプト, モンキー, よう, リターン, リファレンス, リンク, ワーク, 下記, 人工, 今回, 使用, 出力, 削減, 動作, 土台, 場合, 必要, 指示, 新規, 明日, 時間, 最初, 案件, 無料, 現場, 生成, 知能, 短縮, 確実, 自分, 返却, 開発, 間違い,

laravelはenvで設定するのがベターですけど、そうではない環境もenvで
2025.07.11
おはようございます.laravelはenvで設定するのがベターですけど、そうではない環境もenvで構築した方が良いです、なぜかと言えばテスト環境との切り替えが簡単になるということなんです.
使い方はこんな感じまずComposerのライブラリをインストールします.
composer require vlucas/phpdotenv
次に使用方法はこんな感じです.こうしとけば結構楽になりますし使いまわしの時に便利です、Laravel(ララベル)ってライブラリの纏まりで構築されているのでワードプレスの環境でも組み込む事は可能です.自分が使用しているのはenv周りとデータベース周りのライブラリです.
require_once __DIR__ . '/vendor/autoload.php';
use Dotenv\Dotenv;
// .env ファイルを読み込む
$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->load();
// 環境変数の取得
echo getenv('APP_NAME'); // ✅ OK
echo $_ENV['APP_NAME']; // ✅ OK
echo env('APP_NAME'); // ❌ Laravel外では未定義
そうそう.envをルート上に置いている場合は.htaccessに下記の記述を書くことをお忘れなく!!これを置いていない場合は筒抜けになります(エンジンエックスの場合も記載しときますね).
<Files .env>
require all denied
</Files>
location ~ /\.env {
deny all;
}
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, インストール, エックス, エンジン, こと, これ, データベース, テスト, ファイル, プレス, ベター, ライブラリ, ララベル, ルート, ワード, 下記, 使い方, 使用, 便利, 切り替え, 取得, 可能, 周り, 場合, 変数, 定義, 感じ, 方法, 明日, 構築, 環境, 筒抜け, 簡単, 自分, 記載, 記述, 設定,

英語学習にお金を使うように考えを変えます.今まで何度も
2025.07.04
おはようございます.英語学習にお金を使うように考えを変えます.今まで何度も挫折してきた英語学習にお金をかけようと思っています.自分が望むものはサブスクでスマホやPCで勉強が学べるものです.
そういうものを考えたときに浮かぶのがスタディサプリです.スタディサプリみたいなアプリが他にもないのか調べてみました.
自分で調べるのは面倒なのでGeminiさんに調べてもらいました、下記が返答結果になります.
スタディサプリ以外にも、オンラインで学習できるサービスはたくさんあります。対象年齢や学習内容によって様々な選択肢があるので、ご自身の目的やレベルに合わせて検討してみてください。
いくつか代表的な類似サービスやカテゴリーを挙げます。
大手予備校・通信教育系のオンラインサービス
- Z会: 小学生から高校生、大学受験生まで幅広い層に対応した通信教育の老舗。映像授業と丁寧な添削指導が特徴です。
- 進研ゼミ: ベネッセが提供する、小・中・高校生向けの通信教育。タブレット学習やAIを活用した個別最適化された学習が強みです。
- 東進ハイスクール在宅受講コース / 東進オンライン学校: 有名講師による質の高い映像授業が特徴。大学受験に強いです。
- 学研プライムゼミ: 高校生・浪人生向けのハイレベルな映像授業が中心。難関大学対策に特化しています。
- 河合塾One: 河合塾が提供するオンライン学習サービスで、短時間で手軽に学べるコンテンツが特徴です。
AI学習・個別最適化に特化したサービス
- アタマプラス (atama+): AIが生徒の理解度に合わせて最適なカリキュラムを生成し、個別指導のように学習を進められるサービスです。塾や学校への導入も進んでいます。
- すらら: AIが生徒の学習状況を分析し、最適な問題を提供。特に苦手克服や学習習慣の定着に力を入れています。無学年式なので、さかのぼり学習も可能です。
- スマイルゼミ: タブレットを活用した小・中学生向けの通信教育。AIが学習状況に合わせた問題を出題し、定着を促します。
資格取得・ビジネススキル系のオンライン学習サービス
- Schoo (スクー): ビジネススキルやITスキルなど、幅広い分野のオンライン授業をライブ配信や録画で提供しています。
- Udemy (ユーデミー): 世界中の講師が様々な分野の講座を販売しているプラットフォーム。買い切り型で、セール時には大幅割引もあります。
- スタディング: 簿記やITパスポートなど、資格取得に特化したオンライン講座を多数提供しています。
- ユーキャン / ヒューマンアカデミー / 資格のキャリカレ / フォーサイト など: 各種資格取得に特化した通信講座を提供しています。
その他
- N予備校: KADOKAWAとドワンゴが運営する、高校生向けのオンライン学習サービス。多様な講座やプログラミング学習もできます。
- Classi (クラッシー): 学校で導入されていることが多い学習支援プラットフォームで、学習動画やドリルなどが提供されます。
自分で調べるより本当にチャットAIで調べたほうが早いですね便利です、ちなみに生成AIに勉強を教えてもらうという方法もあるようです、自分もある程度身についたらそういう方向にシフトしていくつもりでいますが、何せ土台がガタガタなのでお金をかけて勉強をしようと思っています.
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, アカデミー, アタマ, アプリ, いくつ, オンライン, お金, ガタガタ, カテゴリー, カリキュラム, キャリカレ, クラッシー, コース, こと, コンテンツ, サービス, サブスク, サン, シフト, スキル, スクー, スタディサプリ, スタディング, スマイル, スマホ, セール, セミ, その他, たくさん, タブレット, チャット, つもり, とき, ドリル, ドワンゴ, ハイスクール, ハイレベル, パスポート, ビジネス, ヒューマン, フォーサイト, プライム, プラス, プラットフォーム, プログラミング, ベネッセ, ほう, みたい, もの, ユーキャン, ユーデミー, よう, ライブ, レベル, 丁寧, 下記, 世界中, 中心, 予備校, 代表, 以外, 便利, 個別, 克服, 内容, 出題, 分析, 分野, 割引, 勉強, 動画, 取得, 受講, 受験, 受験生, 可能, 各種, 向け, 問題, 土台, 在宅, 多数, 多様, 大学, 大幅, 大手, 学年, 学校, 学研, 学習, 定着, 対応, 対策, 対象, 導入, 小・中学生, 小学生, 年齢, 強み, 手軽, 指導, 挫折, 授業, 提供, 支援, 教育, 方向, 方法, 明日, 映像, 最適, 有名, 東進, 検討, 様々, 河合塾, 活用, 浪人, 添削, 特徴, 状況, 理解, 生徒, 生成, 目的, 短時間, 簿記, 結果, 習慣, 老舗, 考え, 自分, 自身, 苦手, 英語, 講師, 講座, 販売, 資格, 返答, 通信, 運営, 選択肢, 配信, 録画, 難関, 面倒, 類似, 高校生,

AIで記事を学習して新たな記事を生み出すにはお金が必要だと思っていたがそうでも.
2025.06.22
おはようございます.AIで記事を学習して新たな記事を生み出すにはお金が必要だと思っていたがそうでもなくローカルPCでそこら辺に落ちているLlamaモデルを持ってきてチューニングすれば何とかなるじゃねぇという思いに至った.
実はあなたの手元にあるPCと、そこら中に「落ちている」オープンソースのAIモデル、特にLlama 3があれば、十分記事が生成できるんです。
ローカルAI記事生成は、もはや夢物語じゃない
「AIで記事生成」と聞くと、SFのような世界や、大企業だけが使える特権のように感じるかもしれません。しかし、今は違います。オープンソースの強力な言語モデル、特にMetaが公開したLlama 3の登場は、この常識を大きく覆しました。
Llama 3は、その性能の高さにもかかわらず、誰でも無料で利用できるという点が最大の魅力です。さらに、80億パラメータの8Bモデルであれば、最新のゲーミングPCとまではいかなくとも、ある程度の性能を持つPCであれば十分に動作します。これにより、高額なクラウドサービスを利用せずとも、自分のPCでAI記事生成の環境を構築することが現実的になりました。
なぜLlama 3があなたのPCと相性抜群なのか?
Llama 3がローカルPCでの記事生成に適している理由はいくつかあります。
- 完全無料のオープンソース: 利用に費用がかからないため、予算を気にせずAIを試したり、本格的に導入したりできます。
- 選べるモデルサイズ: Llama 3には様々なサイズのモデルがあり、PCのスペックに合わせて選べます。特に8Bモデルは、個人利用に最適なバランスを持っています。
- 活発な開発者コミュニティ: 世界中の開発者がLlama 3を使った新しいツールや効率的なチューニング方法を日々共有しています。困ったときには助けを借りられる心強い味方です。
- 「量子化」でさらに軽量に: モデルのサイズを大幅に小さくする「量子化」という技術を使えば、より少ないメモリでLlama 3を動かせるようになります。これにより、より多くのPCで利用の道が開けます。
あなたのPCを「記事生成マシン」に変える秘訣
もちろん、いきなりプロのライター並みの記事をAIに書かせるのは難しいかもしれません。しかし、ちょっとした工夫で「何とかなる」レベルの記事生成は十分に可能です。
- 少量のデータでファインチューニング: 大量の記事データは不要です。あなたが書きたい記事のテーマやスタイルに合った、質の良い記事を数十〜数百程度集めてLlama 3を学習(ファインチューニング)させれば、その分野に特化した記事生成能力が格段に向上します。
- プロンプト(指示文)の工夫: AIへの「指示の出し方」は非常に重要です。具体的で明確なプロンプトを与えることで、チューニングが完璧でなくても、驚くほど質の高い記事が生成できます。これはまるで、優秀なアシスタントに的確な指示を出すようなものです。
- 効率的な学習方法の活用: 「LoRA(Low-Rank Adaptation)」のような効率的なファインチューニング手法を使えば、少ないGPUメモリでも短時間でモデルを特定のタスクに最適化できます。
あなたの創造性が、今、AIで加速する
かつては一部の専門家や企業にしか手の届かなかったAIによる記事生成が、今やあなたのPCで実現できる時代になりました。これはまさにAI技術の「民主化」です。
とまぁそういう訳なので何とかしてみますが、ファインチューニングにどれぐらい時間がかかるのかが未知数だったりする.
ファインチューニングPythonコード
以下のPythonコードは、Llama 3モデルをロードし、提供されたテキスト記事でファインチューニング(LoRA使用)を実行し、結果を保存します。 上記の入力値は、このコードに自動的に反映されます。 このコードをPythonファイル(例: `finetune_llama.py`)として保存し、実行してください。
import os
import torch
from datasets import load_dataset
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig, TrainingArguments, Trainer
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training, TaskType
# GPUの利用可能性を確認
print("GPUが利用可能か確認中...")
if not torch.cuda.is_available():
print("GPUが見つかりません。Fine-tuningにはGPUが強く推奨されます。")
# GPUがない場合は、ここでスクリプトを終了するか、CPUモードで続行するか選択できます。
# exit("GPUがないため終了します。")
else:
print(f"GPUが利用可能です: {torch.cuda.get_device_name(0)}")
# --- 1. モデルとトークナイザーのロード ---
# Llama 3モデルのパスを指定します。Hugging Faceのモデル名(例: "meta-llama/Llama-3-8B")
# またはローカルにダウンロードしたモデルのパスを指定してください。
MODEL_NAME = "meta-llama/Llama-3-8B" # ユーザーが入力したパスがここに挿入されます
print(f"モデルとトークナイザーをロード中: {MODEL_NAME}")
# 4bit量子化設定 (GPUメモリの節約に役立ちます)
# bnb_4bit_compute_dtypeは、Ampere以降のNVIDIA GPUに推奨されるbfloat16を使用しています。
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4", # NF4 (NormalFloat4) 量子化タイプ
bnb_4bit_compute_dtype=torch.bfloat16
)
# トークナイザーをロード
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
# Llama 3はデフォルトでbos_tokenを付与しないことがあるため、明示的に追加。
# また、padding_side='right'はLlamaモデルに推奨される設定です。
tokenizer.pad_token = tokenizer.eos_token
tokenizer.padding_side = "right"
# モデルをロードし、量子化設定を適用し、自動的にGPUにマッピングします。
model = AutoModelForCausalLM.from_pretrained(
MODEL_NAME,
quantization_config=bnb_config,
device_map="auto", # 利用可能なデバイス(GPU)に自動的にモデルを分散
trust_remote_code=True # リモートコードの実行を許可
)
print("モデルロード完了。")
# k-bit学習用にモデルを準備 (PEFTライブラリのため)
# gradient_checkpointingを有効にすることで、メモリ使用量をさらに削減できます。
model.gradient_checkpointing_enable()
model = prepare_model_for_kbit_training(model)
print("k-bit学習用にモデルを準備しました。")
# --- 2. データセットの準備 ---
# あなたのテキスト記事ファイルが格納されているディレクトリを指定します。
# 例: 'your_article_data/' の中に 'article1.txt', 'article2.txt', ... と置かれている場合
DATA_DIR = "./your_article_data/" # ユーザーが入力したパスがここに挿入されます
print(f"データセットをロード中: {DATA_DIR}")
# 'text'形式でデータセットをロードします。指定されたディレクトリ内のすべての.txtファイルを読み込みます。
# 各ファイルが1つのエントリとして扱われます。
try:
dataset = load_dataset('text', data_files={'train': os.path.join(DATA_DIR, '*.txt')})
print(f"データセットのサンプル数: {len(dataset['train'])}")
except Exception as e:
print(f"データセットのロード中にエラーが発生しました。ディレクトリとファイル形式を確認してください: {e}")
exit("データセットロード失敗。")
# データセットをトークン化する関数
# 長い記事をモデルの最大入力長に分割します。
def tokenize_function(examples):
# Llama 3の最大入力長は通常8192ですが、お使いのGPUのVRAMに合わせて調整してください。
# ここでは一般的な値として2048を設定しています。
max_length = 2048
# truncate=Trueで最大長を超えるテキストを切り捨てます。
return tokenizer(examples["text"], truncation=True, max_length=max_length)
# データセットをトークン化します。
# num_procはCPUコア数に応じて並列処理を行い、処理を高速化します。
tokenized_dataset = dataset.map(
tokenize_function,
batched=True,
num_proc=os.cpu_count(),
remove_columns=["text"] # 元のテキスト列は学習に不要になるため削除します。
)
print("データセットのトークン化が完了しました。")
# --- 3. PEFT (LoRA) の設定 ---
# LoRA (Low-Rank Adaptation) は、元のモデルの重みをフリーズし、
# 小さなアダプター層を追加して学習させることで、効率的にファインチューニングを行います。
# これにより、GPUメモリの使用量を抑えつつ、高い性能を実現できます。
lora_config = LoraConfig(
r=16, # LoRAのランク。値を大きくすると表現力が増すが、メモリ消費も増える。
lora_alpha=32, # LoRAのスケーリング係数。rの2倍程度が推奨されることが多いです。
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], # LoRAを適用する層。Llamaモデルで一般的な層。
bias="none", # バイアスを学習しない設定。
lora_dropout=0.05, # ドロップアウト率。過学習を防ぐために設定します。
task_type=TaskType.CAUSAL_LM, # タスクタイプを因果言語モデルに設定。
)
# モデルにLoRAアダプターを追加します。
model = get_peft_model(model, lora_config)
print("モデルにLoRAアダプターを適用しました。")
model.print_trainable_parameters() # 学習可能なパラメータ数を確認します。
# --- 4. 学習の実行 ---
# 学習済みモデルを保存するディレクトリ
OUTPUT_DIR = "./llama3_finetuned_model/" # ユーザーが入力したパスがここに挿入されます
# 学習の設定
training_args = TrainingArguments(
output_dir=OUTPUT_DIR,
num_train_epochs=3, # エポック数。データセットのサイズと希望する精度に応じて調整してください。
per_device_train_batch_size=1, # GPUあたりのバッチサイズ。VRAMが少ない場合は1に設定。
gradient_accumulation_steps=4, # 勾配を蓄積するステップ数。実質的なバッチサイズは per_device_train_batch_size * gradient_accumulation_steps になります。
optim="paged_adamw_8bit", # 8bit AdamWオプティマイザを使用し、メモリ効率を向上させます。
save_steps=500, # 500ステップごとにモデルを保存します。
logging_steps=100, # 100ステップごとにログを出力します。
learning_rate=2e-4, # 学習率。
fp16=True, # 混合精度学習を有効化 (GPUが対応している場合)。VRAM削減と高速化に寄与します。
max_steps=-1, # num_train_epochsに基づいて学習します。
group_by_length=True, # 同じ長さのシーケンスをグループ化し、パディングを削減します。
lr_scheduler_type="cosine", # 学習率スケジューラーのタイプ。
warmup_ratio=0.03, # ウォームアップ比率。
report_to="none", # レポート先を指定しない (wandbなどを使用しない場合)。
)
# トレーナーの初期化
# data_collatorは、モデルの入力形式に合わせてデータを整形します。
trainer = Trainer(
model=model,
train_dataset=tokenized_dataset["train"],
args=training_args,
data_collator=lambda data: {
'input_ids': torch.stack([f['input_ids'] for f in data]),
'attention_mask': torch.stack([f['attention_mask'] for f in data]),
'labels': torch.stack([f['input_ids'] for f in data]), # 因果言語モデルでは、入力自体がラベルとなります。
},
)
# 学習の開始
print("Fine-tuningを開始します...")
trainer.train()
print("Fine-tuningが完了しました。")
# --- 5. 学習済みモデルの保存 ---
# LoRAアダプターのみを保存します。これにより、ファイルサイズが小さく、効率的に管理できます。
trainer.save_model(OUTPUT_DIR)
print(f"学習済みLoRAアダプターが '{OUTPUT_DIR}' に保存されました。")
# 保存したアダプターを使って推論を行う方法の例 (コメントアウトされています):
# このコードは、ファインチューニング後にモデルをロードして推論を行うための参考例です。
# from peft import PeftModel
#
# # 元のモデルをロード (学習時と同じ量子化設定を使用します)
# base_model = AutoModelForCausalLM.from_pretrained(
# MODEL_NAME,
# quantization_config=bnb_config,
# device_map="auto",
# trust_remote_code=True
# )
#
# # 保存したLoRAアダプターを元のモデルに結合します。
# peft_model = PeftModel.from_pretrained(base_model, OUTPUT_DIR)
#
# # 推論モードに設定します。
# peft_model.eval()
#
# # テキスト生成の例
# prompt = "ローカルPCでのLlama 3ファインチューニングの利点とは"
# inputs = tokenizer(prompt, return_tensors="pt").to("cuda") # 入力をGPUに移動
#
# with torch.no_grad(): # 勾配計算を無効化し、メモリ使用量を削減
# outputs = peft_model.generate(
# **inputs,
# max_new_tokens=200, # 生成する新しいトークンの最大数
# do_sample=True, # サンプリングによる生成を有効化
# top_p=0.9, # Nucleusサンプリングの閾値
# temperature=0.7, # 生成の多様性を制御する温度
# eos_token_id=tokenizer.eos_token_id # 終了トークンID
# )
# print("\n--- 生成されたテキスト ---")
# print(tokenizer.decode(outputs[0], skip_special_tokens=True))
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, ;;), )。, アウト, アシスタント, アダプター, あたり, アップ, あなた, いくつ, ウォーム, エポック, エラー, エントリ, オープン, オプティマイザ, お金, クラウドサービス, グループ, クン, ゲーミング, コード, コア, ここ, こと, コミュニティ, コメント, これ, サイズ, サンプリング, サンプル, シーケンス, スクリプト, スケーリング, スケジューラー, スタイル, ステップ, スペック, すべて, ソース, そこら, タイプ, ダウンロード, タスク, ため, チューニング, ツール, データ, データセット, テーマ, ディレクトリ, テキスト, デバイス, デフォルト, トー, トークナイザー, とき, どれ, トレーナー, ドロップ, バイアス, パス, バッチ, パディング, パラメータ, バランス, ファイル, ファイルサイズ, ファインチューニング, ファインチューニングコード, フリーズ, プロ, プロンプト, マシン, マッピング, メモリ, モード, モデル, もの, ユーザー, よう, ライター, ライブラリ, ラベル, ランク, リモート, レベル, レポート, ローカル, ロード, ログ, 一般, 一部, 上記, 不要, 世界, 世界中, 並み, 並列, 予算, 付与, 以下, 以降, 企業, 使い, 使用, 係数, 保存, 個人, 優秀, 入力, 公開, 共有, 具体, 処理, 出力, 分割, 分散, 分野, 初期, 利点, 利用, 制御, 削減, 削除, 創造, 加速, 助け, 効率, 動作, 勾配, 十分, 参考, 反映, 可能, 向上, 味方, 因果, 場合, 多様, 夢物語, 大幅, 大量, 失敗, 学習, 完了, 完全, 完璧, 実現, 実行, 実質, 寄与, 対応, 専門, 導入, 少量, 工夫, 希望, 常識, 強力, 形式, 必要, 思い, 性能, 手元, 手法, 技術, 抜群, 指定, 指示, 挿入, 推奨, 推論, 提供, 整形, 新た, 方法, 日々, 明日, 明確, 明示, 時代, 時間, 最大, 最新, 最適, 有効, 未知数, 本格, 格段, 格納, 構築, 様々, 比率, 民主, 活用, 活発, 消費, 混合, 済み, 温度, 準備, 無効, 無料, 特定, 特権, 現実, 理由, 環境, 生成, 発生, 登場, 的確, 相性, 短時間, 確認, 秘訣, 移動, 程度, 管理, 節約, 精度, 終了, 結合, 結果, 続行, 能力, 自体, 自分, 自動的, 蓄積, 表現, 言語, 計算, 記事, 設定, 許可, 調整, 費用, 軽量, 追加, 通常, 適用, 選択, 重み, 重要, 量子, 開始, 開発, 関数, 閾値, 非常, 高速, 高額, 魅力,

x.gdのURL短縮機能を利用するためのコードを作りましたので、お裾分け.
2025.06.08
おはようございます.x.gdのURL短縮機能を利用するためのコードを作りましたので、お裾分けです、コードは下記になります.ドキュメントをぱぱっと見て作った感じなので不具合などが潜んでいるかもしれないけど恐らく大丈夫そうという事で木曜日の早朝にXでコードをシェアした形になります.
なお、x.gdからAPIの発行を行う作業をお忘れなく(こちらからAPIを発行).
因みに自分はt2t.jpというドメインを私有していて、こちらで自分専用の短縮URLを発行出来るようにしています.この頃は自分が作った短縮URLをあまり使ってないので、こちらをサービス化しようかな等と考えていたりします.
因みにどうするかは未定ですけどね.もしみんなが使用出来るようにしたら、このサイトでも告知します.
<?php
require 'config.php';
class ShortUrl
{
public $apiKey = null;
public $endPoint = 'https://xgd.io/V1/shorten?';
public function __construct($api_key = null)
{
$this->apiKey = $api_key;
return $this;
}
public function getShortUrl($url = null, $shortid = null, $analytics = true, $filterbots = false)
{
if (empty($this->apiKey)) {
return json_encode(['status' => 0, 'message' => 'api-keyがありません']);
}
if (empty($url)) {
return json_encode(['status' => 0, 'message' => 'URLがありません']);
}
if (isset($url) && !preg_match('/^http[s]?:\/\/(www\.)?/', $url)) {
return json_encode(['status' => 0, 'message' => 'URLが無効です']);
}
if (isset($shortid) && !preg_match('/^([0-9a-zA-Z_]{6,15})$/', $shortid)) {
return json_encode(['status' => 0, 'message' => '短縮コードが無効です']);
}
if (!is_bool($analytics)) {
$analytics = true;
}
if (!is_bool($filterbots)) {
$filterbots = false;
}
$parameter = [
'key' => $this->apiKey,
'url' => $url,
'shortid' => $shortid,
'analytics' => $analytics ? 'true' : 'false',
'filterbots' => $filterbots ? 'true' : 'false'
];
$response = @file_get_contents($this->endPoint . http_build_query($parameter));
return $response;
}
}
if($argv[1]){
$url = $argv[1];
$shortid = isset($argv[2]) ? $argv[2] : null;
$analytics = isset($argv[3]) && $argv[3] === 'true' ? true : false;
$filterbots = isset($argv[4]) && $argv[4] === 'true' ? true : false;
echo (new ShortUrl(APIKEY))->getShortUrl($url, $shortid, $analytics, $filterbots);
}
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, お裾分け, コード, こちら, サービス, サイト, シェア, そう, ため, ドキュメント, ドメイン, みんな, よう, 下記, 不具合, 作業, 使用, 利用, 告知, 大丈夫, 専用, 感じ, 早朝, 明日, 木曜日, 未定, 機能, 無効, 発行, 短縮, 私有, 自分,

万博の来場者数と5月23日以降の予測をバイブコーディングで作ってみました
2025.05.31
おはようございます.万博の来場者数と5月23日以降の予測をバイブコーディングで作ってみましたのでコードをシェアします.大阪万博の来場者数はYahooニュースの記事のデータを元にしています、其処から今後の予測を立ててもらいました.
https://zip358.com/tool/EXPO2025-OSAKA-KANSAI-JAPAN
因みに予測は簡易的なものなので、恐らく大きくハズレるとは思いますがそれなりの表が生成されるのが凄いなと思います.生成AIはOpenAIの無料枠を使用してデータと次のデータを元に10月13日までの予測を出力してという指示とJavaScriptやCSSを使用してグラフ化してという指示を出しただけで、コードを書いてくれます.
const labels = [];
const allVisitors = [];
const generalVisitors = [];
const staffVisitors = [];
const rawData = [
["4/13", 146426, 22000], ["4/14", 70488, 17000], ["4/15", 63719, 16000], ["4/16", 73869, 15000],
["4/17", 82692, 15000], ["4/18", 93908, 15000], ["4/19", 108773, 15000], ["4/20", 95524, 16142],
["4/21", 99638, 17352], ["4/22", 103729, 17099], ["4/23", 99140, 17795], ["4/24", 108888, 17376],
["4/25", 110759, 17591], ["4/26", 122102, 17299], ["4/27", 102015, 16985], ["4/28", 121282, 17465],
["4/29", 97559, 17813], ["4/30", 101397, 17621], ["5/1", 105945, 17548], ["5/2", 104805, 17741],
["5/3", 120696, 17886], ["5/4", 136805, 16289], ["5/5", 126371, 16804], ["5/6", 76517, 16935],
["5/7", 91688, 16932], ["5/8", 105449, 18578], ["5/9", 105782, 19813], ["5/10", 128918, 21373],
["5/11", 121667, 18268], ["5/12", 117658, 17856], ["5/13", 123640, 18087], ["5/14", 129527, 17668],
["5/15", 129456, 17762], ["5/16", 132817, 17700], ["5/17", 123974, 16464], ["5/18", 117000, 16000],
["5/19", 129000, 17000], ["5/20", 125000, 18000], ["5/21", 129000, 19000], ["5/22", 134000, 18000],
["5/23", 157000, 18000]
];
rawData.forEach(([date, total, staff]) => {
labels.push(date);
allVisitors.push(total);
staffVisitors.push(staff);
generalVisitors.push(total - staff);
});
const futureDays = 143;
for (let i = 1; i <= futureDays; i++) {
const futureDate = new Date(2025, 4, 13 + i); // 4月13日からスタート
const label = `${futureDate.getMonth() + 1}/${futureDate.getDate()}`;
labels.push(label);
// 予測ロジック: 最近5日間の平均にランダムなゆらぎを加える
const recentGeneral = generalVisitors.slice(-5);
const avgGeneral = recentGeneral.reduce((a, b) => a + b, 0) / recentGeneral.length;
const fluctuation = Math.sin(i / 5) * 5000 + (Math.random() - 0.5) * 8000;
const predictedGeneral = Math.round(avgGeneral + fluctuation);
const predictedStaff = 18000 + Math.round(Math.sin(i / 7) * 1000);
const predictedTotal = predictedGeneral + predictedStaff;
generalVisitors.push(predictedGeneral);
staffVisitors.push(predictedStaff);
allVisitors.push(predictedTotal);
}
const ctx = document.getElementById('visitorChart').getContext('2d');
const chart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: '来場者数(合計)',
data: allVisitors,
borderColor: 'rgba(75, 192, 192, 1)',
backgroundColor: 'rgba(75, 192, 192, 0.1)',
borderWidth: 2,
tension: 0.3,
fill: true
},
{
label: '一般来場者数',
data: generalVisitors,
borderColor: 'rgba(255, 159, 64, 1)',
backgroundColor: 'rgba(255, 159, 64, 0.1)',
borderWidth: 2,
tension: 0.4,
fill: true
},
{
label: '関係者数',
data: staffVisitors,
borderColor: 'rgba(153, 102, 255, 1)',
backgroundColor: 'rgba(153, 102, 255, 0.1)',
borderWidth: 2,
tension: 0.4,
fill: true
}
]
},
options: {
animation: {
duration: 1500,
easing: 'easeInOutQuart'
},
responsive: true,
scales: {
y: {
title: {
display: true,
text: '人数'
}
},
x: {
title: {
display: true,
text: '日付'
},
ticks: {
maxRotation: 90,
minRotation: 45,
maxTicksLimit: 50
}
}
},
plugins: {
legend: {
position: 'top',
},
title: {
display: false
}
}
}
});
これは脅威だなって思う人もいると思いますが、自分は便利だなって思う方です.今後、数年でエンジニア職を奪われかねないという懸念もありますが、コード書きは無くならないじゃないかなって思う方です.理由は修正しないといけない事が必ず起きる事.動作はするが予期しない動作が発生した場合、素人には対応できないため.
未来は人が描いている斜め上の未来になる事が多く、どう転ぶかは「神のみぞ知る」だと思います.なのであまり脅威に思わずそうなった時に対応するというスタンスで良いのかなと.
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, エンジニア職, スタンス, バイブコーディング, ミゾ, ランダム, 万博, 予測, 予測ロジック, 人数, 其処, 動作, 大阪万博, 懸念, 指示, 日付, 月日, 月日以降, 生成, 脅威,

デジタルアドレスAPIのコードを書いてみました.書いたのは
2025.05.30
おはようございます.デジタルアドレスAPIのコードを書いてみました.書いたのは生成AIだけど一度で上手く正しいコードが生成出来たわけではなくて二、三回の指示出しを行って下記のコードが生成されました.
デジタルアドレスのAPIを使用するには企業もしくは個人事業者で屋号を登録されている方でないとAPIのアカウント登録は今のところ出来ないので、自分はリファレンスと生成AIが出力したコードを見て恐らく正しく処理されるだろうと思ったのでgist.githubに公開しました.
尚、引数にIPアドレスを渡さないといけない所があるけれど、これはサーバーのIPアドレスになります.
<?php
class JapanPostAPIClient
{
private string $clientId;
private string $secretKey;
private string $clientIp;
private ?string $accessToken = null;
private ?array $lastResponse = null;
public function __construct(string $clientId, string $secretKey, string $clientIp)
{
$this->clientId = $clientId;
$this->secretKey = $secretKey;
$this->clientIp = $clientIp;
}
public function authenticate(): self
{
$url = 'https://api.da.pf.japanpost.jp/api/v1/j/token';
$data = json_encode([
'grant_type' => 'client_credentials',
'client_id' => $this->clientId,
'secret_key' => $this->secretKey
]);
$headers = [
"Content-Type: application/json",
"x-forwarded-for: {$this->clientIp}"
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpcode !== 200) {
throw new Exception("Token request failed with status {$httpcode}: {$response}");
}
$responseData = json_decode($response, true);
$this->accessToken = $responseData['token'] ?? null;
$this->lastResponse = $responseData;
return $this;
}
public function searchCode(string $searchCode, array $params = []): self
{
if (!$this->accessToken) {
throw new Exception("Access token is not set. Please call authenticate() first.");
}
$defaultParams = [
'page' => 1,
'limit' => 10,
'choikitype' => 1,
'searchtype' => 1
];
$queryParams = http_build_query(array_merge($defaultParams, $params));
$url = "https://api.da.pf.japanpost.jp/api/v1/searchcode/{$searchCode}?{$queryParams}";
$headers = [
"Authorization: Bearer {$this->accessToken}",
"Accept: application/json"
];
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
throw new Exception("Search request failed with status {$httpCode}: {$response}");
}
$this->lastResponse = json_decode($response, true);
return $this;
}
public function getJson(): string
{
return json_encode($this->lastResponse, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
}
public function getArray(): ?array
{
return $this->lastResponse;
}
}
// 使い方の例:
// $client = new JapanPostAPIClient('YOUR_CLIENT_ID', 'YOUR_SECRET_KEY', 'IPアドレス.xxx.xxx.xxx');
// echo $client->authenticate()->searchCode('1000001')->getJson();
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, アカウント登録, アドレス, コード, サーバー, デジタルアドレス, リファレンス, 下記, 企業, 使い方, 例, 個人事業者, 屋号, 引数, 所, 指示出し, 正しいコード, 生成, 自分,

トランプ大統領の2025年からの任期をカウントするJSコードです.
2025.05.24
おはようございます.トランプ大統領の2025年からの任期をカウントするコードです.まだまだトランプ政権は始まったばかりなんですが、もう自分としてはあんま嬉しい気持ちはしていません.いま儲かっているのはデイトレードを行っている人達かも知れないです.
// トランプ大統領の2025年からの任期をカウント
const termStart = new Date('2025-01-20');
const termEnd = new Date('2029-01-20');
const today = new Date();
const totalTermDays = Math.floor((termEnd - termStart) / (1000 * 60 * 60 * 24));
if (today < termStart) {
console.log('任期はまだ始まっていません。');
} else if (today > termEnd) {
const servedDays = totalTermDays;
console.log('任期は終了しています。');
console.log(`トランプ大統領は ${servedDays} 日間、在任しました。`);
} else {
const servedDays = Math.floor((today - termStart) / (1000 * 60 * 60 * 24));
const remainingDays = Math.floor((termEnd - today) / (1000 * 60 * 60 * 24));
console.log(`任期中です。(${today.toISOString().split('T')[0]} 現在)`);
console.log(`在任日数: ${servedDays} 日`);
console.log(`残り日数: ${remainingDays} 日`);
console.log(`任期全体の日数: ${totalTermDays} 日`);
}
秒でウクライナとロシアの戦争を止めると言っていたのに、泥沼化しています.トランプさんになってからアメリカは前より酷くなったのではとか思っていますが、アメリカ国民は満足しているのでしょうか🤔.
知らないけれど、結構たいへんそうな気がしています.
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, アメリカ, アメリカ国民, あんま嬉しい気持ち, ウクライナ, カウント, コード, ディトレード, トランプ大統領, トランプ政権, ロシア, 任期, 任期中, 任期全体, 在任日数, 戦争, 日数, 気, 泥沼化, 秒,

配列で返して変数で受け取る事を分割代入というであってる.
2025.05.16
おはようございます.配列で返して変数で受け取る事を分割代入という…どんなコードか具体的に書いたのが下記です.
<?php
function getArray(): array {
return [10, 20];
}
[$x, $y] = getArray();
echo "x: " . $x . "\n";
echo "y: " . $y . "\n";
// 要素数が合わない場合
function getMoreArray(): array {
return [100, 200, 300];
}
[$p, $q] = getMoreArray(); // $q には 200 が代入され、300 は無視される
echo "p: " . $p . "\n";
echo "q: " . $q . "\n";
[$m, , $n] = getMoreArray(); // カンマで要素をスキップ
echo "m: " . $m . "\n";
echo "n: " . $n . "\n";
// 連想配列のようなキー指定も可能 (PHP 7.1以降)
function getAssocArray(): array {
return ['name' => '太郎', 'age' => 30];
}
['name' => $userName, 'age' => $userAge] = getAssocArray();
echo "名前: " . $userName . "\n";
echo "年齢: " . $userAge . "\n";
?>
最初、凄くこれに違和感を覚えたのを覚えています.理由はカッコカッコで括ってあるから、なんだか配列を連想するのですよね.とても便利ですけどね、因みにPHP言語でも比較的に新しいバージョンだったら使用できます.
尚、PHPバージョン7.1以下だった場合はlistを使用すると同じようなことになります.
<?php
function getArray(): array {
return [10, 20];
}
list($x, $y) = getArray();
echo "x: " . $x . "\n";
echo "y: " . $y . "\n";
// 要素数が合わない場合
function getMoreArray(): array {
return [100, 200, 300];
}
list($p, $q) = getMoreArray(); // $q には 200 が代入され、300 は無視される
echo "p: " . $p . "\n";
echo "q: " . $q . "\n";
list($m, , $n) = getMoreArray(); // カンマで要素をスキップ
echo "m: " . $m . "\n";
echo "n: " . $n . "\n";
?>
それにしても分割代入って呼び名、直ぐに忘れそう・・・.
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, $代, カッコカッコ, カンマ, キー指定, コード, スキップ, バージョン以下, 分割代, 分割代入, 呼び名, 変数, 太郎, 新しいバージョン, 最初, 要素, 言語, 連想配列, 違和感, 配列,

tMDbのAPIを使用して今まで観た映画をリスト化してみましたよ.
2025.04.27
おはようございます.tMDbのAPIを使用して今まで観た映画をリスト化してみましたよ.sqliteの構造は下記になります.idはbigintと記載していますが、AUTO_INCREMENTの間違いです🐼.

Xにも呟きましたがAPIを使用して何かするのはこの頃飽きてきて触らなかったのですが、たまには触れてみようとコードを書いているうちに簡単に作れるようになってしまって少々物足りなさを感じています.
次回はAPIを使う側ではなくて作る側になろうと思っています.GW期間になると思いますが600円のレンタルサーバーのフリードメインを使ってツマラナイAPIみたいな失笑系を作ってみます.
なお、ソースコードの解説がなくてすみません、このソースコードを生成AIに投げてどのような処理か説明して言えば恐らく正解を教えてくれます.
明日へ続く
#真相をお話します.
少年と犬
Flow
curl -sS https://getcomposer.org/installer | php
composer require guzzlehttp/guzzle
composer require illuminate/database
<?php
require_once 'config.php';
require_once 'vendor/autoload.php';
use Illuminate\Database\Capsule\Manager as Capsule;
use Illuminate\Database\Capsule\Manager as DB;
class MovieList
{
public $result = '';
public $imgBasicUrl = 'https://image.tmdb.org/t/p/w500';
public $filePath = FILEPATH_DIR_;
public function __construct($filePath='')
{
$filePath = $filePath?$filePath:$this->filePath;
if (!file_exists($filePath.'/movie-list.db')) {
exit;
}
$capsule = new Capsule();
$capsule->addConnection([
'driver' => 'sqlite',
'database' => $filePath.'/movie-list.db',
'prefix' => '',
]);
$capsule->setAsGlobal();
$capsule->bootEloquent();
}
public function getMovieList(string $q): void
{
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', 'https://api.themoviedb.org/3/search/multi?query=' . $q . '&include_adult=false&language=ja-JP&page=1', [
'headers' => [
'Authorization' => 'Bearer ' . API_KEY,
'accept' => 'application/json',
],
]);
$this->result = $response->getBody();
}
public function getResult(): void
{
$data = DB::table('list')->get();
$lists = [];
foreach ($data as $key => $value) {
$result = unserialize($value->value);
if(isset($result?->media_type) && $result->media_type == 'movie'){
$lists[] = [
'id'=>$data[$key]->id,
'title'=>$result->title,
'img'=>$this->imgBasicUrl . $result->poster_path,
];
}
}
print json_encode($lists);
}
public function saveCsvlist(): void
{
$fileData = explode("\n",file_get_contents('movie-list.csv'));
$fileData = array_reverse($fileData);
foreach($fileData as $line)
{
foreach(explode(',',$line) as $data){
$q = urlencode($data);
$movieList = new MovieList();
$movieList->getMovieList($q);
foreach((json_decode($movieList->result))?->results as $result){
DB::table('list')->insert([
'value' => serialize($result)
]);
}
}
}
}
public function delete($id): void
{
DB::table('list')->where('id', '=', $id)->delete();
print json_encode(['res'=>'true']);
}
}
//(new MovieList())->saveCsvlist();
if($_SERVER['REQUEST_METHOD'] == 'POST')
{
if(!isset($_POST['id'])){
(new MovieList())->getResult();
}else{
//(new MovieList())->delete($_POST['id']);
}
}
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, コード, ソースコード, ツマラナイ, フリードメイン, レンタルサーバー, 側, 円, 処理, 失笑系, 少年, 構造, 次回, 正解, 犬, 生成, 真相,

404画像をそのまま残しておくとSEOに影響があるのでどうするか?#php
2025.04.23
おはようございます.404画像をそのまま残しておくとSEOに影響があるのでどうするか?php の場合、下記のコードで404ページに飛んでいるかどうかの確認は出来る.でも毎回、確認しているとページを表示している時に遅延が発生してとても良い処理とは言えないなのでDBに画像あるなしが確認できるテーブルを作ります.
<?php
class HttpCodeChecker
{
public function getHttpStatusCode(string $url): int|false
{
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_exec($ch);
if (curl_errno($ch)) {
curl_close($ch);
return false; // エラーがあった場合
}
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $httpCode;
}
}
画像のステータス確認できるコードで定期的にページをクロールしてステータスをテーブルに保存(更新)することにより404画像によってSEOに影響を与えない作りになります.
他にも方法はあるけど、レンタルサーバーではこの方法がベストプラクティスな解なのかもしれない.クロールするのが出来ない場合は一度、画像URLをJsonデータで出力してフロントエンド側で画像有無を判断し存在するものだけを表示するという案もあるのだけども、今のSEO的にはあまりオススメはしない.
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, エラー, コード, ステータス, データ, テーブル, フロントエンド側, ベストプラクティス, レンタルサーバー, 下記, 保存, 影響, 方法, 案, 毎回, 画像, 画像有無, 確認, 良い処理, 遅延,

FF14のエオルゼア時間を表示するツールのソースコードを作りましたのでお裾分け.
2025.04.10
おはようございます.先人の知恵を拝借して人工知能にぶん投げてソースコードを作っていただきました.尚、ツワモノさんがいて天候などもいつ起きるのか把握できるそうです.どういうアルゴリズムなのかは見ていないので分からないけど自分でも可能そうな気もします.
因みにソースコードをこちらに貼り付けておきますのでご自由にご使用ください.それにしてもオンラインゲームなのに自分の狩り場には人っ子一人いないのが悲しいです.
function lt2et(date) {
// UNIX時間(秒)を取得
const ut = date.getTime() / 1000;
// エオルゼア時間1/1/1 00:00:00から経過した秒数
const et_seconds = (ut * 60.0) / 175.0;
// 各エオルゼア単位を計算
const et_hours = ut / 175.0;
const et_days = et_hours / 24.0;
const et_months = et_days / 32.0;
const et_year = et_months / 12.0 + 1.0;
const et_month = (et_months % 12.0) + 1.0;
const et_date = (et_days % 32.0) + 1.0;
const et_hour = et_hours % 24.0;
const et_minute = et_seconds % 60.0;
return {
year: Math.floor(et_year),
month: Math.floor(et_month),
date: Math.floor(et_date),
hour: Math.floor(et_hour),
minute: Math.floor(et_minute),
};
}
setInterval(() => {
let date;
date = new Date(); // 現在時刻
const et = lt2et(date);
// 出力(例: 0001-01-01 00:00)
document.getElementById("output").textContent = `
${et.month.toString().padStart(2, "0")}-${et.date
.toString()
.padStart(2, "0")} ${et.hour.toString().padStart(2, "0")}:${et.minute
.toString()
.padStart(2, "0")}
`;
}, 0);
Lv30なったらチョコボに乗れるらしいのでいまはそれを楽しみにしてソロで狩り続けています.
最後にZen foxで活動しています.
※Hades【Mana】で活動しています.
フォロー等よろしくお願いいたします🙇.
https://jp.finalfantasyxiv.com/lodestone/character/34913141/
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, アルゴリズム, エオルゼア時間, オンラインゲーム, ソースコード, ソロ, チョコボ, ツワモノさん, 人工知能, 例, 出力, 各エオルゼア単位, 天候, 時刻, 最後, 狩り場, 知恵, 秒, 秒数, 計算,

何もわからない??デフォルト. null合体演算子
2025.03.30
おはようございます.??(クエスチョンマーク二つ)ってのはnull合体演算子と言います.PHP以外にもあります.三項演算子などは知っている人いると思いますが、null合体演算子は知らない人もちらほらいるかもです.
因みにいま関わっているシステムには出てきますね.自分が開発にジョインしたときに自分もnull合体演算子を使用したことはあります.
三項演算子はまぁ適度に使用しますが深い階層にはしません.昔もあまり深い階層にはしなかったと思いますがしていたら後釜に迷惑が行ったかもです、そんな感じで深い階層にするのはあまり可読性が宜しく無い気がします.
null合体演算子は三項演算子よりもわかりやすいかもです.nullだったら別の値に変えてしまう処理だと思ってください.
サンプルコードリンクを貼っときます.
https://www.php.net/manual/ja/language.operators.comparison.php#language.operators.comparison.coalesce
尚、null合体演算子は優先順位が低いそうですね.そのため下記のようなことする時はカッコで囲んであげないと正しい結果が得られないそうです.算術とかでもそうみたい.
<?php
// $name が未定義の場合、警告が発生します。
print 'Mr. ' . $name ?? 'Anonymous';
// "Mr. Anonymous" と出力
print 'Mr. ' . ($name ?? 'Anonymous');
?>
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, かっこ, クエスチョンマーク二つ, サンプルコードリンク, す算術, 三項演算子, 値, 優先順位, 処理, 出力, 可読性, 合体演算子, 後釜, 昔, 未定義, 正しい結果, 深い階層, 警告, 迷惑, 開発,

アナログ時計をTOPページに追加しました.ソースコードはこちら.
2025.03.26
おはようございます.アナログ時計をTOPページに追加しました.ソースコードはこちらに記載していきます.尚、ソースコードは生成AI、Grokを使用して制作しました.
<div class="clock">
<div class="hand hour-hand"></div>
<div class="hand minute-hand"></div>
<div class="hand second-hand"></div>
<div class="center-clock"></div>
</div>
.clock {
/* width: 200px;
height: 200px;
border: 2px solid rgb(226, 226, 226); */
border-radius: 50%;
}
.hand {
position: absolute;
bottom: 50%;
left: 50%;
transform-origin: bottom;
background: #333!important;
}
.hour-hand {
width: 4px;
height: 60px;
background: #333!important;;
}
.minute-hand {
width: 3px;
height: 80px;
background: #666!important;;
}
.second-hand {
width: 2px;
height: 90px;
background: rgb(94, 93, 93)!important;;
}
.center-clock {
width: 10px;
height: 10px;
background: rgb(95, 95, 95)!important;;
border-radius: 50%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 4;
}
function updateClock() {
const now = new Date();
const hours = now.getHours();
const minutes = now.getMinutes();
const seconds = now.getSeconds();
// 角度の計算
const hourDeg = (hours % 12 + minutes / 60) * 30; // 12時間で360度
const minuteDeg = (minutes + seconds / 60) * 6; // 60分で360度
const secondDeg = seconds * 6; // 60秒で360度
// 針の回転
document.querySelector('.hour-hand').style.transform = `translateX(-50%) rotate(${hourDeg}deg)`;
document.querySelector('.minute-hand').style.transform = `translateX(-50%) rotate(${minuteDeg}deg)`;
document.querySelector('.second-hand').style.transform = `translateX(-50%) rotate(${secondDeg}deg)`;
}
// 初回実行
updateClock();
// 1秒ごとに更新
setInterval(updateClock, 1000);
生成AIが最初登場した時はここまで出来なかったので一回目の指示でちゃんとしたものを制作していけるようになったというのは正直なところ驚きです.やっぱり指数関数的に人工知能は成長するのかなとたまに思うことがありますが、それと相反する思いも持っています.
相反するとは、あと5年ぐらいで人工知能も頭打ちになるじゃないかなとも思っています.
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, アナログ時計, ソースコード, ところ, 人工知能, 分, 初回実行, 回転, 年, 度, 指数, 指示, 最初, 生成, 秒, 秒ごと, 角度, 計算, 針, 頭打ち,

クラシックAPIを使用してGitHub APIから草データを取得.
2025.03.12
おはようございます.クラシックAPIを使用してGitHub APIから草データを取得するコードをAPI使用しつつ手直してモノの数分で完成しました.こういうのを思うと少人数の開発でも希望を持てるなって思います.ちなみに自分は未だに無料枠でコードを生成してもらっています.
正直なところ、無料枠で事が足りるという印象ですね.コードの改善や命名は生成AIにやってもらった方が優秀です、自分の無能がよくわかります.
草データの使用方法はタイトル通りなので特に問題ないかなって思います.コンフィグファイルは書かずしても変数に代入してあげれば良いわけですからね.一応、github上にソースコードを掲載しています.
https://github.com/taoka3/GitHubGrass

良かったらいいね👍️(⭐️)宜しくお願い致します.
<?php
class GitHubGrass
{
private string $token;
private string $username;
private array $weeks = [];
private int $cellSize = 12;
private int $padding = 2;
private array $colors = [];
public function __construct(string $username, string $token)
{
$this->username = $username;
$this->token = $token;
// 色の設定(GitHub風)
$this->colors = [
'level0' => [235, 237, 240],
'level1' => [155, 233, 168],
'level2' => [64, 196, 99],
'level3' => [48, 161, 78],
'level4' => [33, 110, 57]
];
}
/**
* GitHub APIから草データを取得
*/
public function fetchContributions(): bool
{
$url = 'https://api.github.com/graphql';
$query = <<<'JSON'
{
user(login: "USERNAME") {
contributionsCollection {
contributionCalendar {
weeks {
contributionDays {
contributionCount
}
}
}
}
}
}
JSON;
// ユーザー名を埋め込む
$query = str_replace("USERNAME", $this->username, $query);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'Authorization: Bearer ' . $this->token,
'User-Agent: ContributionsApp'
]);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(['query' => $query]));
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
if (!isset($data['data']['user']['contributionsCollection']['contributionCalendar']['weeks'])) {
$this->weeks = []; // 空の配列を設定
return false;
}
$this->weeks = $data['data']['user']['contributionsCollection']['contributionCalendar']['weeks'];
return true;
}
/**
* 草画像を生成
*/
public function generateImage(): string
{
if (count($this->weeks)) {
die('No data available.');
}
// 画像サイズ設定
$width = (count($this->weeks) * ($this->cellSize + $this->padding)) + $this->padding;
$height = (7 * ($this->cellSize + $this->padding)) + $this->padding;
// 画像作成
$image = imagecreatetruecolor($width, $height);
$bgColor = imagecolorallocate($image, 255, 255, 255);
imagefill($image, 0, 0, $bgColor);
// 色の作成
$colorPalette = [];
foreach ($this->colors as $key => $rgb) {
$colorPalette[$key] = imagecolorallocate($image, ...$rgb);
}
// セル描画
foreach ($this->weeks as $x => $week) {
foreach ($week['contributionDays'] as $y => $day) {
$count = $day['contributionCount'];
$color = $this->getColor($count, $colorPalette);
// 四角形を描画
imagefilledrectangle(
$image,
$x * ($this->cellSize + $this->padding) + $this->padding,
$y * ($this->cellSize + $this->padding) + $this->padding,
($x + 1) * ($this->cellSize + $this->padding),
($y + 1) * ($this->cellSize + $this->padding),
$color
);
}
}
// 出力
ob_start();
imagepng($image);
$imageData = ob_get_contents();
ob_end_clean();
imagedestroy($image);
$base64 = base64_encode($imageData);
return $base64;
}
/**
* コントリビューション数に応じた色を取得
*/
private function getColor(int $count, array $colorPalette)
{
if ($count == 0) {
return $colorPalette['level0'];
} elseif ($count < 5) {
return $colorPalette['level1'];
} elseif ($count < 10) {
return $colorPalette['level2'];
} elseif ($count < 20) {
return $colorPalette['level3'];
} else {
return $colorPalette['level4'];
}
}
}
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, $代, から草データ, クラシック, コード, コントリビューション数, コンフィグファイル, セル描画, ソースコード, 出力, 命名, 四角形, 変数, 少人数, 手直し, 描画, 未だ, 生成, 配列,

画像の存在有無確認用.NodeJSで動く一式を共有します😌.
2025.02.21
おはようございます.画像の存在有無確認用.NodeJSで動く一式を共有します😌.先日の朝ポストしたものの一式のファイルになります、URLはダミーです.また、このプログラムコードはあるKサイトの画像の存在有無確認のために作ったものになります.
https://github.com/taoka3/check-images-app
このプログラムコードをカスタマイズすることによって、404ページへリダイレクトするようなページでも画像の存在有無を確認することが可能になります.
因みにこのコードでは大量に画像存在有無を確認する場合には適していない為、修正が必要になります.どこを修正しないといけないかは、ご自身でコードを修正してお試しください.ヒントは同期処理を非同期処理にという事です.また、このコードでコンソールに結果を出力していますが、業務などで使用する場合はCSVなどに出力するのが適切なのかも知れません.
const { loadImage } = require("@napi-rs/canvas");
function checkImages(name,imageUrls)
{
imageUrls.forEach(async (imgUrl) => {
try {
// canvasを使って画像が存在するか確認
await loadImage(imgUrl); // 画像をロードして存在確認
console.log(`name: ${name}, url: ${imgUrl}, exists: true`);
} catch (error) {
console.log(`name: ${name}, url: ${imgUrl} exists: false `);
}
});
}
プログラムコードは、朝にちゃちゃっと作ったものになります.なのでエレメントが無かったら落ちたりします.そういう欠陥はあるものの簡易的に画像の存在有無を確認したい場合などには役立つかと思います.
明日へ続く
著者名
@taoka_toshiaki
※この記事は著者が40代前半に書いたものです.
Profile
高知県在住の@taoka_toshiakiです、記事を読んで頂きありがとうございます.
数十年前から息を吸うように日々記事を書いてます.たまに休んだりする日もありますがほぼ毎日投稿を心掛けています😅.
SNSも使っています、フォロー、いいね、シェア宜しくお願い致します🙇.
SNS::@taoka_toshiaki
タグ
```, エレメント, コード, コンソール, ヒント, ファイル, プログラムコード, 一式, 修正, 同期処理, 存在有無, 存在有無確認, 存在有無確認用, 存在確認, 朝, 業務, 欠陥, 画像存在有無, 結果, 非同期処理,