Laravelでファイルアップロード
「ファイルのアップロード」がどういう流れで行われるのか?
- ブラウザ(クライアント)側:
<!-- ファイル送信用のフォーム -->
<form method="POST" enctype="multipart/form-data">
<input type="file" name="uploadFile">
<button>送信</button>
</form>
enctype="multipart/form-data"
という部分がないとファイルを正しく送信できません。
なぜなら:
- ファイルには画像やPDFなど、テキスト以外のデータ(バイナリデータ)が含まれます
- そのため、特別な送信方式(multipart/form-data)が必要になります
- ファイル送信:ヘッダー情報(メタデータ)とファイルデータが分かれて送信される
- これは、ファイルには画像やPDFなど、テキスト以外のデータが含まれるため、特別な形式(multipart/form-data)が必要になるためです。
▼プレビュー
ユーザーがこのフォームで「ファイルを選択」して「アップロード」ボタンを押すと、選択したファイルがサーバーに送られます。
<ファイルアップロードのフロー>
<POSTリクエストの違い>
<サーバーサイドのフロー>
Feature BranchでのGitマージ戦略:安全なコード統合のためのベストプラクティス
Gitを使用したチーム開発において、feature branchとmain branchの統合は重要なポイントとなります。この記事では、安全かつ効率的なブランチ統合の戦略について解説します。
なぜFeature Branchで先にマージするのか?
Feature branchで開発を進める中で、main branchの変更を早めに取り込むことには、いくつかの重要な利点があります:
- コンフリクトの早期発見
- 段階的な統合テストが可能
- 最終的なmainブランチへの統合がスムーズ
具体的な手順
1. Mainの変更を取り込む
まず、feature branchで作業中に、main branchの変更を取り込みます:
# 現在のブランチを確認
git branch
# mainの変更を取り込む
git merge main
2. コンフリクト対応
この時点でコンフリクトが発生した場合の対処:
- コンフリクトが発生したファイルの確認
- 各ファイルのコンフリクトを解決
- 解決したファイルをステージング
git add <コンフリクトを解決したファイル>
- マージの完了
git commit -m "Resolve conflicts with main"
テストと確認
コンフリクト解決後の重要なステップ:
- 機能が正常に動作するか確認
- テストの実行
- 必要に応じて追加の修正を実施
4. Mainブランチへの統合
すべての確認が完了したら、main branchへの統合を行います:
# mainブランチに切り替え
git checkout main
# featureの変更を取り込む
git merge feature
このアプローチのメリット
- リスク管理
- コンフリクトを早期に発見し、feature branch上で解決できる
- 問題が発生しても、main branchには影響が及ばない
- 品質管理
- feature branch上で十分なテストが可能
- 統合前に動作確認を完了できる
- スムーズな統合
- 最終的なmain branchへの統合がスムーズになる
- 大きな問題が発生するリスクが低減される
まとめ
この方法は、特に以下のような状況で効果を発揮します:
- 長期間に渡る開発プロジェクト
- 複数人で同じコードベースを扱う場合
- クリティカルな機能の開発時
早めにmainの変更を取り込んで確認することで、最終的な統合をより安全に行うことができます。これは、モダンなGit開発フローにおける重要なプラクティスの一つと言えるでしょう。
【Python】バックエンドの理解とフロントエンド開発のポイント
Pythonの実行環境について
Docker環境があれば、Python実行環境を新たにインストールする必要はありません
バックエンドとの連携
確認すべき重要な点:
APIエンドポイントの情報
- APIのURLとパス
- 各エンドポイントの機能
- HTTPメソッド(GET, POST等)
- 必要なリクエストヘッダー
データ形式
// 例:こういった形でAPIの型定義を作成すると良い
interface ApiResponse {
status: string;
data: {
id: number;
name: string;
// その他の項目
}[];
}
.gitignore
ファイルの更新:
Copynode_modules
.env
.env.local
.DS_Store
package.json
のscripts
セクションを確認/追加:
jsonCopy{
"scripts": {
"start": "node src/index.js",
"build": "npm install",
"vercel-build": "npm run build"
}
}
デプロイ手順:
- GitHubにリポジトリを作成:
bashCopygit init
git add .
git commit -m "Initial commit"
git remote add origin <your-github-repo-url>
git push -u origin main
- Vercelでのデプロイ設定:
- vercel.com にGitHubアカウントでログイン
- 「New Project」をクリック
- GitHubリポジトリをインポート
- プロジェクト設定で環境変数を設定: Copy
AWS_ACCESS_KEY_ID=xxxxx AWS_SECRET_ACCESS_KEY=xxxxx AWS_REGION=ap-northeast-1
- 「Deploy」をクリック
これで自動的にデプロイが開始され、完了すると公開URLが提供されます。その後はGitHubにプッシュするたびに自動デプロイが実行されます。
【WordPress】Atuaテーマ
Atuaテーマ概要
「Atua」というテーマは比較的新しく、インストール数も多くなくあまりメジャーではないといえます。
そのため情報が少なく下記の手段で調べる必要がありそうです
- テーマの公式ページ
- 製作者サイト
- GitHubリポジトリ(もしあれば)でカスタマイズガイドやREADMEファイル
参考に「Lightning」と比較し見ると下記です
Atua | Lightning | |
---|---|---|
バージョン | 1.0.98 | 15.29.0 |
Active installations | 600+ | 100,000+ |
PHP version | 5.6 | 7.4 |
WordPress version | 4.7 | 6.4 |
Atua の子テーマ Atus でカスタマイズ
下記ページでテーマファイルはダウンロード可能です
子テーマをインストールするだけで大分デザインが変更されます
Atua 独自で作成した子テーマでカスタマイズ
wp-content/themes/atua-child/を作成
ディレクトリ構成
atua-child
┗┳ style.css
┣ archive.php
┣
style.css
/*
Theme Name: atua Child
Template: atua
*/
WordPress管理画面でテーマ追加画面で子テーマとして認識されます
(注意)テーマを有効化するとフロントページのコンテンツが表示されなくなってしまいます。
archive.php
<?php
/**
* The template for displaying archive pages.
*
* @link https://codex.wordpress.org/Template_Hierarchy
*
* @package Atua
*/
get_header();
?>
<style>
.dt_post_item {
margin-bottom: 2rem;
}
.dt_post_item > a {
display: flex;
align-items: center;
column-gap: 1rem;
}
.post-thumbnail-wrapper {
flex-shrink: 0;
position: relative;
width: 18%;
aspect-ratio: 16 / 9;
overflow: hidden;
}
.post-thumbnail-wrapper img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
</style>
<section class="dt_posts dt-py-default">
<div class="dt-container">
<div class="dt-row dt-g-5">
<?php if ( !is_active_sidebar( 'atua-sidebar-primary' ) ): ?>
<div class="dt-col-lg-12 dt-col-md-12 dt-col-12 wow fadeInUp">
<?php else: ?>
<div class="dt-col-lg-8 dt-col-md-12 dt-col-12 wow fadeInUp">
<?php endif; ?>
<?php if( have_posts() ): ?>
<?php
// Start the loop.
while( have_posts() ) : the_post(); ?>
<div class="dt_post_item wow fadeInUp animated" data-wow-delay="100ms" data-wow-duration="1500ms">
<?php
// 元のテーマファイルのコメントアウト
// get_template_part('template-parts/content','page-2');
?>
<a href="<?php the_permalink(); ?>">
<div class="post-thumbnail-wrapper">
<?php
if ( has_post_thumbnail() ) {
the_post_thumbnail('medium');
} else {
echo '<img src="https://www.shoshinsha-design.com/wp-content/uploads/2020/05/noimage-760x460.png" alt="Default Image">';
}
?>
</div>
<?php the_title(); ?>
</a>
</div>
<?php endwhile;
// End the loop.
// Pagination.
the_posts_pagination( array(
'prev_text' => '<i class="fa fa-angle-double-left"></i>',
'next_text' => '<i class="fa fa-angle-double-right"></i>'
) );
// If no content, include the "No posts found" template.
else:
get_template_part('template-parts/content','none');
endif; ?>
</div>
<?php get_sidebar(); ?>
</div>
</div>
</section>
<?php get_footer(); ?>
WordPressサイト全体を非表示にする一般的な方法
プラグインでメンテナンス表示
「Maintenance」プラグイン
.htaccess
ファイルで全体にアクセス制限をかける
1. .htaccess
ファイルを編集
FTPソフトを使ってWordPressのルートディレクトリ(wp-config.php
がある場所)にある .htaccess
ファイルを編集します。
# メンテナンスモード設定開始
RewriteEngine On
# メンテナンスページ (maintenance.html) へのアクセスは除外
RewriteCond %{REQUEST_URI} !/maintenance.html$
# 全てのアクセスを maintenance.html にリダイレクト
RewriteRule ^(.*)$ /maintenance.html [R=302,L]
# メンテナンスモード設定終了
サブディレクトリ環境での修正方法
# WordPressが /wordpress/ にインストールされている場合
RewriteEngine On
# maintenance.html へのアクセスは除外
RewriteCond %{REQUEST_URI} !^/wordpress/maintenance.html$
# すべてのアクセスを maintenance.html にリダイレクト
RewriteRule ^(.*)$ /wordpress/maintenance.html [R=302,L]
2. maintenance.html の作成
WordPressのルートディレクトリに maintenance.html
ファイルを作成し、メンテナンス案内ページとしてアップロードします。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>メンテナンス中</title>
<!-- CSSスタイルで簡単なデザイン -->
<style>
body {
text-align: center;
font-family: Arial, sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
}
.container {
margin-top: 20%;
}
h1 {
color: #333;
font-size: 2em;
}
p {
color: #666;
font-size: 1.2em;
}
</style>
</head>
<body>
<div class="container">
<h1>メンテナンス中</h1>
<p>現在、サイトはメンテナンス中です。しばらくお待ちください。</p>
</div>
</body>
</html>
WordPressの「設定」で検索エンジンから非表示にする
- WordPress管理画面にログイン。
- 「設定」→「表示設定」→「検索エンジンでの表示」の項目で「検索エンジンがサイトをインデックスしないようにする」にチェック。
- 「変更を保存」ボタンをクリック。
注意: この方法は「検索エンジン」には非表示にできますが、直接URLにアクセスすれば閲覧可能です。
【Amazon WorkSpaces】トラブル、対処法
「WorkSpaceは数秒で準備完了になります…」から変わらない
- 上記画面で終了してしまう
- 朝スマホのデザリングで問題発生
- 夜自宅Wifiですると問題なく接続できる
- 他のスマホのデザリングでは接続できる
- 問題のスマホ再起動で接続可能に
VBAの実践的なテクニックを網羅的に解説
マクロとVBAの概念
「マクロ」は2つの意味があります
「マクロ」はExcelを自動処理してくれるもExcelの機能です。
そして「VBA(Visual Basic for Applications)」という言語で命令書を作成することを「マクロを組む」といいます。
マクロはどうやって作成するか?
- 自動で記録(マクロ記録)
- 自分で書く(VBE(Visual Basic Editor)というツール)…Excelで[Alt] + [F11]
マクロの記録方法
再度押すと記録再開されます。
記録したマクロを使用する方法
記録したマクロを選択し、「実行」すると記録した処理が実行されます
「編集」でVBEを起動して編集できます
VBEを開く手順
開発タブを表示させる
「開発」の項目はExcelインストールしたばかりの状態だとだとはチェックが外れいるので有効化しておきます。
表示された「開発」タブから「Visual Basic Editor」をクリック
[Alt] + [F11]でも開けます
マクロのセキュリティレベル
Excelのセルやワークシートを操作できるだけでなく、悪意のある者が作成すればパソコンに障害を引き起こすような動作もプログラミングすることができます。
そうした悪意のあるマクロが勝手に実行されないように、セキュリティレベルを設定する機能があります。
[ファイル] → [オプション] → [トラスト センター]
既定では1度目のみ警告が表示される
VBEでプロシージャを実行すると警告が出る
プローシージャとは
マクロ動作の基本単位「プロシージャ」には「Sub」と「Function」があります。
サブ(Sub)とは
**サブプロシージャ(Sub)**は、「処理手順のまとまり」を表すひとまとまりのコードブロックです。「この手順を実行してね」と指示できる単位と考えると分かりやすいです。Subは処理を実行するための命令文のかたまりであり、戻り値を返しません。
サブの基本形
Sub サブ名()
' ここに実行したい処理を書きます
End Sub
処理内容はインデント(字下げ)するのがお作法です
Function
で始まりEnd Function
で終わる- Function名を処理結果として代入することで戻り値を設定します(
関数名 = 値
) - 戻り値は数値でも文字列でもオブジェクトでも、データ型を適宜指定できます
- Functionはワークシート関数としても使える場合があり、Excelのセルに独自の関数として呼び出せます(ただしパブリック関数として標準モジュールに記述が必要)
- 別のSubやFunctionから「
戻り値 = 関数名(引数)
」という形で呼び出して、計算結果などを受け取ることができます
【Amazon Bedrock】【Node.js】生成AIを使用したVBAフォーマッター
プロジェクトの内容
Amazon Bedrockの生成AIを活用した、VBAマクロコードを自動的にフォーマットするWebアプリケーション。インデント、変数名、コメントなどを統一的なルールで整形します。
機能概要
- VBAコードのフォーマット
- インデントの自動調整
- 変数名の標準化
- コメントの追加
- ヘッダー・フッターの挿入
技術スタック
- Node.js
- Express.js
- HTML/CSS/JavaScript
必要要件
- Node.js (v14以上)
- npm (v6以上)
セットアップ手順
- プロジェクト構成ファイルを作成
- 依存パッケージのインストール
npm install
- 開発サーバーの起動
npm run dev
- ブラウザでアクセス
http://localhost:3000
プロジェクト構成
vba-formatter/
├── src/
│ ├── index.js # メインサーバーファイル
│ ├── formatter.js # 基本的なフォーマット処理
│ ├── bedrock/ # Bedrock関連の新規ディレクトリ
│ │ ├── client.js # Bedrock クライアント設定
│ │ ├── prompts.js # プロンプトテンプレート
│ │ └── analyzer.js # コード分析ロジック
│ └── public/ # フロントエンド
Bedorock関連
.env
AWS_ACCESS_KEY_ID=あなたのアクセスキー
AWS_SECRET_ACCESS_KEY=あなたのシークレットキー
AWS_REGION=us-east-1
dotenv
は、.env
ファイルを読み込んで、その内容を環境変数としてprocess.env
に設定するためのパッケージです。
npm install dotenv
@aws-sdk/client-bedrock-runtime
: Amazon Bedrockと通信するため下記インストール
npm install dotenv @aws-sdk/client-bedrock-runtime
client.js
:Amazon Bedrockへの接続設定require('dotenv').config();
const { BedrockRuntimeClient } = require("@aws-sdk/client-bedrock-runtime");
// Bedrockクライアントの設定
const client = new BedrockRuntimeClient({
region: process.env.AWS_REGION || "us-east-1",
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
}
});
module.exports = client;
prompts.js
:AIへの指示(プロンプト)テンプレート/**
* prompts.js
* AIへの指示内容を定義するファイル
* VBAコードの分析と改善の方法をAIに指示します
*/
// AIへの指示内容を定義(バッククォートで囲むことで複数行の文字列を作成)
const ANALYSIS_PROMPT = `
# ここからがAIへの指示内容です
=============================================
# AIの役割定義
あなたはVBAコードの専門家として、以下のコードを分析し、優先順位に従って改善してください。
# 入力コード部分
\`\`\`vba
{code}
\`\`\`
# 改善要件(優先順位:高 - 第一弾)
=============================================
1. インデント処理
- SUBからENDSUBまでTABひとつ分のインデント
- IF文、With文、For文等の制御構造にもインデント
- ネストレベルに応じて適切なインデント
2. ヘッダー・フッター追加
- Sub開始時のヘッダー:
Option Explicit
'***************************************************
' [プロシージャ名]
'***************************************************
'【機 能】[機能の説明]
'【引 数】[引数の説明]
'【戻 り 値】[戻り値の説明]
'【機能説明】[詳細な説明]
'【備 考】[その他特記事項]
' Copyright(c) 2024 Your Company All Rights Reserved.
'***************************************************
- 処理の区切り:
'***************************************************
' 変数宣言
'***************************************************
[変数宣言部分]
- Sub終了時のフッター:
'***************************************************
'---------------------------------------------------
' Version
'---------------------------------------------------
' 1.00 | 2024.01.01 | ********* | *********
'***************************************************
# 改善要件(優先順位:中 - 第二弾)
=============================================
3. 変数名の改善
- 一文字の変数を禁止
- セル参照の変数は意味のある名前に変更
(例: Last_Row, Last_Column)
- 配列はArrayを付ける
- その他の変数はDataを付ける
4. Public変数・Call文の処理
- Public変数にはPublic関数であることをコメントで記載
- Call文にはモジュール名を追加
(例: Call Module1.印刷)
5. 変数宣言のコメント
- 宣言した変数の横にコメントを追加
- 使用目的や内容を簡潔に説明
# 改善要件(優先順位:中 - 第三弾)
=============================================
6. 変数・シート名の処理
- 日本語変数名を英語に変更
- 未宣言変数を「変数宣言」セクションに追加
- 型判定できない変数はVariantに
- シート名は定数(Const)で定義
- シート追加時の名前はVariantで処理
7. コメント追加
- IF文、With文などの制御構造にコメント
- 配列の内容説明
- SET文の説明
- Offsetのコメント必須
(参照セルと目的を明記)
8. コードの最適化
- 類似コードが3回以上続く場合はループ化
- パスの直書きは避け、pathに置換
(元のパスはコメントとして保持)
# 出力形式
=============================================
優先順位の高い要件(第一弾)から順に適用し、
改善したVBAコードのみを出力してください。
`;
module.exports = {
ANALYSIS_PROMPT
};
analyzer.js
:Bedrockとの通信処理// AWS SDKからBedrockのクライアントとコマンドをインポート
const { InvokeModelCommand } = require("@aws-sdk/client-bedrock-runtime");
const client = require('./client');
const { ANALYSIS_PROMPT } = require('./prompts');
/**
* VBAコードをClaudeモデルで分析する関数
* @param {string} vbaCode - 分析対象のVBAコード
* @returns {Promise<string>} - 改善されたVBAコード
*/
async function analyzeCode(vbaCode) {
try {
// プロンプトテンプレートにVBAコードを挿入
const prompt = ANALYSIS_PROMPT.replace('{code}', vbaCode);
// Claude用のリクエストコマンドを作成
const command = new InvokeModelCommand({
// Claude 3 Sonnetモデルを指定
modelId: "anthropic.claude-3-sonnet-20240229-v1:0",
contentType: "application/json",
accept: "application/json",
// Claude用のリクエスト形式
body: JSON.stringify({
anthropic_version: "bedrock-2023-05-31",
max_tokens: 2048,
messages: [
{
role: "user",
content: prompt
}
]
})
});
// BedrockAPIを呼び出し
const response = await client.send(command);
// レスポンスの処理(Claude形式)
const responseBody = Buffer.from(response.body).toString('utf8');
const jsonResponse = JSON.parse(responseBody);
// Claudeのレスポンス形式から結果を抽出
if (jsonResponse.content && jsonResponse.content[0]) {
return jsonResponse.content[0].text;
}
throw new Error('AIモデルからの応答が不正な形式です');
} catch (error) {
console.error("コード分析エラー:", error);
throw error;
}
}
module.exports = {
analyzeCode
};
メインファイル
src/index.js
:Webサーバーのメイン処理// ========================================
// サーバーのメインファイル (index.js)
// VBAフォーマッターのバックエンド処理を担当
// ========================================
// 必要なモジュールのインポート
const express = require('express');
const { analyzeCode } = require('./bedrock/analyzer'); // Bedrock AIの分析機能
const { formatVBA } = require('./formatter'); // 基本的なフォーマット機能
const { ANALYSIS_PROMPT } = require('./bedrock/prompts'); // AIプロンプトの取得
// Expressアプリケーションの初期化
const app = express();
// ミドルウェアの設定
// 静的ファイル(HTML/CSS/JS)の提供設定
app.use(express.static('src/public'));
// JSONリクエストの解析を有効化
app.use(express.json());
/**
* AIへのプロンプト内容を提供するエンドポイント
* フロントエンドでAIへの指示内容を表示するために使用
*/
app.get('/prompts', (req, res) => {
res.json({
success: true,
prompt: ANALYSIS_PROMPT
});
});
/**
* VBAコード分析API
* フロントエンドから送信されたコードを分析・改善
*/
app.post('/format', async (req, res) => {
try {
// リクエストからコードを取得
const { code } = req.body;
// 入力チェック
if (!code) {
return res.status(400).json({
success: false,
error: 'コードが提供されていません'
});
}
// 1. 基本的なフォーマットを適用
const basicFormatted = formatVBA(code);
// 2. AIによる高度な分析と改善
const aiAnalysis = await analyzeCode(basicFormatted);
// 成功時のレスポンス
res.json({
success: true,
formatted: aiAnalysis
});
} catch (error) {
// エラー発生時の処理
console.error('エラーが発生しました:', error);
res.status(500).json({
success: false,
error: error.message
});
}
});
// サーバーの起動設定
const PORT = process.env.PORT || 3000;
// 全てのIPアドレスからのアクセスを許可(社内ネットワーク用)
app.listen(PORT, '0.0.0.0', () => {
console.log('========================================');
console.log(`VBA Formatter サーバー起動`);
console.log(`http://localhost:${PORT}`);
console.log('========================================');
});
フロントエンド
public/index.html
:ユーザーインターフェース<!DOCTYPE html>
<html lang="ja">
<head>
<!-- 文字エンコーディングとビューポートの設定 -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>VBA Formatter</title>
<!-- reset.css ress -->
<link rel="stylesheet" href="https://unpkg.com/ress/dist/ress.min.css" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="style.css">
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@100..900&display=swap" rel="stylesheet">
<!-- スタイルシートの読み込み -->
<link rel="stylesheet" href="style.css">
</head>
<body>
<!-- メインコンテナ -->
<div class="container">
<h1>VBA Formatter</h1>
<!-- AIへの指示内容表示セクション(src/bedrock/prompts.jsの内容) -->
<div class="prompt-section">
<h3>AIへの指示内容</h3>
<!-- プロンプト内容を動的に表示する要素 -->
<div class="prompt-display-container">
<div class="user-icon"></div>
<div class="prompt-display-wrapper">
<pre class="prompt-display">ユーザーからの指示内容がここに表示されます</pre>
</div>
</div>
</div>
<!-- コードエディタ部分 -->
<div class="editors">
<!-- 入力用エディタ -->
<div class="editor">
<h3>分析するコード</h3>
<!-- 入力用テキストエリア:ユーザーがコードを入力または貼り付け可能 -->
<textarea id="input" placeholder="VBAコードを入力してください"></textarea>
<!-- ファイル選択ボタン:.basと.txtファイルのみ受付 -->
<input type="file" id="vbaFile" accept=".bas,.txt">
<!-- <span class="detail">※.basと.txtファイルのみ受付</span> -->
</div>
<!-- 出力用エディタ -->
<div class="editor">
<h3>改善後のコード</h3>
<!-- 出力用テキストエリア:読み取り専用で改善されたコードを表示 -->
<textarea id="output" readonly></textarea>
<!-- フォーマット実行ボタン -->
<button onclick="formatCode()">Format</button>
</div>
</div>
</div>
<!-- JavaScriptの処理 -->
<script>
// ページ読み込み時にプロンプト内容を取得
window.onload = async function() {
try {
const response = await fetch('/prompts');
const data = await response.json();
if (data.success) {
document.querySelector('.prompt-display').textContent = data.prompt;
}
} catch (error) {
console.error('プロンプト取得エラー:', error);
document.querySelector('.prompt-display').textContent = 'プロンプトの読み込みに失敗しました';
}
};
/**
* コードのフォーマットを実行する関数
* 1. 入力を取得
* 2. サーバーにリクエスト
* 3. 結果を表示
*/
async function formatCode() {
// テキストエリアから入力を取得
const input = document.getElementById('input').value;
// ステータス表示用の要素取得
const formatButton = document.querySelector('button');
// 入力値のバリデーション
if (!input) {
alert('VBAコードを入力してください');
return;
}
// 処理開始のステータス表示
formatButton.innerHTML = '<span class="processing"><span></span><span></span><span></span></span>';
try {
// サーバーへのリクエスト
const response = await fetch('/format', {
method: 'POST', // POSTメソッドを使用
headers: {
'Content-Type': 'application/json' // JSONデータとして送信
},
body: JSON.stringify({ code: input }) // 入力をJSONに変換
});
// レスポンスの処理
const data = await response.json();
// 成功時は結果を表示、失敗時はエラーメッセージを表示
if (data.success) {
document.getElementById('output').value = data.formatted;
formatButton.textContent = '分析完了!';
} else {
formatButton.textContent = 'エラー: ' + data.error;
}
} catch (error) {
// 通信エラーなどの例外処理
formatButton.textContent = 'エラー: ' + error.message;
}
formatButton.classList.remove('processing'); // アニメーションクラスを削除
}
/**
* ファイル選択時の処理
* 選択されたファイルの内容を入力エリアに表示
*/
document.getElementById('vbaFile').addEventListener('change', (e) => {
// 選択されたファイルを取得
const file = e.target.files[0];
if (file) {
// FileReaderを使用してファイルの内容を読み込み
const reader = new FileReader();
// ファイル読み込み完了時の処理
reader.onload = (e) => {
// 読み込んだ内容を入力エリアに設定
document.getElementById('input').value = e.target.result;
};
// ファイルをテキストとして読み込み開始
reader.readAsText(file);
}
});
</script>
</body>
</html>
public/style.css
:見た目の装飾body {
font-family: "Noto Sans JP", serif;
background-color: #f1f2f3;
}
/* メインコンテナ */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 2rem 2rem;
}
h1 {
background-color: #fff;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
text-align: center;
letter-spacing: 0.1em;
padding: 1rem 0;
margin: 0 calc(50% - 50vw);
width: 100vw;
margin-bottom: 2rem;
}
h3 {
margin-bottom: 1rem;
text-align: center;
letter-spacing: .1em;
}
/* AIへの指示内容表示部分 */
.prompt-section {
margin-bottom: 2rem;
}
.prompt-display-container {
display: flex;
align-items: end;
justify-content: center;
gap: 2rem;
margin: 0 auto;
}
.user-icon {
width: 40px;
height: 40px;
background-color: #f6ce55;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
margin-bottom: 3rem;
position: relative;
}
.user-icon::after {
content: "";
position: absolute;
top: 96%;
left: 50%;
transform: translateX(-50%);
width: 50px;
height: 40px;
background-color: #f6ce55;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
border-top-left-radius: 50%;
border-top-right-radius: 50%;
}
.prompt-display-wrapper {
max-width: 80%;
display: flex;
align-items: center;
justify-content: center;
background-color: #fff;
border-radius: 5rem 5rem 5rem 0;
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
padding: 2rem;
}
.prompt-display {
font-family: monospace;
font-size: 0.825rem;
line-height: 1.5;
white-space: pre-wrap;
height: 300px;
overflow-y: scroll;
resize: vertical;
}
/* カスタムスクロールバーのスタイル */
.prompt-display::-webkit-scrollbar {
width: 0.5rem; /* スクロールバーの幅 */
}
.prompt-display::-webkit-scrollbar-thumb {
background: #888;
border-radius: 1rem;
}
/* エディタ部分 */
.editors {
display: flex;
gap: 2rem;
margin-bottom: 1rem;
}
.editor {
flex: 1;
display: flex;
flex-direction: column;
}
.editor > * {
margin-bottom: 1rem;
}
.editor > *:last-child {
margin-top: auto!important;
}
.editor input {
height: 3rem;
background-color: #ccc;
padding: 0.5rem 1rem;
border-radius: 4px;
width: 100%;
transition: all 0.3s ease;
}
.editor input:hover {
background-color: #bbb;
}
.editor .detail {
margin-top: 0.5rem;
color: #FF565D;
}
/* テキストエリア */
.editor textarea {
width: 100%;
height: 500px;
font-family: monospace;
padding: 10px;
font-size: 14px;
line-height: 1.5;
border: 2px solid #ccc;
background-color: #fff;
border-radius: 4px;
}
button {
height: 3rem;
padding: 0.5rem 1rem;
background-color: #e9be3b;
;
border-radius: 4px;
color: white;
letter-spacing: 0.1em;
border: none;
cursor: pointer;
}
button:hover {
background-color: #d3b458;
;
transition: all 0.3s ease;
}
/* アニメーションを追加するクラス */
.processing {
display: inline-flex;
gap: 0.5rem;
}
.processing span {
display: inline-block;
width: 0.75rem;
height: 0.75rem;
background-color: #fff;
border-radius: 50%;
animation: fadeDots 1.5s infinite ease-in-out;
}
/* それぞれのドットに異なるアニメーションの遅延を設定 */
.processing span:nth-child(1) {
animation-delay: 0s;
}
.processing span:nth-child(2) {
animation-delay: 0.2s;
}
.processing span:nth-child(3) {
animation-delay: 0.4s;
}
/* 薄さ(透明度)を変化させるアニメーション */
@keyframes fadeDots {
0%, 100% { opacity: 0.2; } /* 薄くなる */
50% { opacity: 1; } /* 濃くなる */
}
その他
formatter.js
:VBAコードの基本的な整形処理
// VBAコードをフォーマットする関数
// 引数:code - フォーマットする元のVBAコードの文字列
// 戻り値:フォーマット済みのVBAコードの文字列
function formatVBA(code) {
// ステップ1: コードを行単位に分割
// splitは文字列を指定した区切り文字(ここでは改行)で配列に分割する
const lines = code.split('\n');
// ステップ2: インデントレベルを管理する変数を初期化
// インデントレベルは、コードのネストの深さを表す
// 例:Sub内なら1、If文の中なら2、というように増えていく
let indentLevel = 0;
// ステップ3: フォーマット済みの行を格納する配列を準備
const formattedLines = [];
// ステップ4: キーワードを定義
// これらの単語が行の先頭にあるかどうかでインデントを判断する
const startKeywords = ['sub', 'function', 'if', 'for', 'do'];
const endKeywords = ['end sub', 'end function', 'end if', 'next', 'loop'];
// ステップ5: 各行を処理
// forEachは配列の各要素に対して処理を行う
lines.forEach((line, lineNumber) => {
// 行の空白を削除して、小文字に変換(比較用)
const trimmedLine = line.trim();
const lowerLine = trimmedLine.toLowerCase();
// ステップ6: 空行の処理
if (trimmedLine === '') {
formattedLines.push('');
return; // 次の行の処理へ
}
// ステップ7: インデントレベルの調整
// 7.1: ブロックの終了を示すキーワードの前にインデントを減らす
if (endKeywords.some(keyword => lowerLine.startsWith(keyword))) {
indentLevel = Math.max(0, indentLevel - 1);
}
// ステップ8: 行のフォーマット
// 8.1: 現在のインデントレベルに応じてスペースを追加
// repeat()は文字列を指定回数繰り返す
const indent = ' '.repeat(indentLevel);
// 8.2: コメントの処理
// シングルクォートで始まる行はコメント
if (trimmedLine.startsWith("'")) {
formattedLines.push(indent + trimmedLine);
} else {
// 8.3: 通常のコードの処理
formattedLines.push(indent + trimmedLine);
}
// ステップ9: 次の行のためのインデントレベルの更新
// 9.1: ブロックの開始を示すキーワードの後でインデントを増やす
if (startKeywords.some(keyword => lowerLine.startsWith(keyword))) {
indentLevel++;
}
});
// ステップ10: フォーマット済みの行を改行文字で結合して1つの文字列に戻す
return formattedLines.join('\n');
}
// ステップ11: ヘッダーコメントを生成する関数
function createHeader() {
return [
"Option Explicit",
"'***************************************************",
"' VBAプログラム",
"'***************************************************",
"'【機 能】",
"'【返 り 値】なし",
"'【機能説明】",
"'【備 考】",
"' Copyright(c) " + new Date().getFullYear(),
"'***************************************************",
""
].join('\n');
}
// ステップ12: 変数をチェックして適切な型を提案する関数
function suggestVariableType(variableName) {
// 変数名から型を推測
if (variableName.toLowerCase().includes('count')) {
return 'Long';
} else if (variableName.toLowerCase().includes('date')) {
return 'Date';
} else if (variableName.toLowerCase().includes('flag')) {
return 'Boolean';
} else if (variableName.toLowerCase().includes('name')) {
return 'String';
} else {
return 'Variant';
}
}
// ステップ13: モジュールの外部公開
// このファイルの関数を他のファイルから使用できるようにする
module.exports = {
formatVBA,
createHeader,
suggestVariableType
};
プロジェクト設定ファイル
package.json
:依存パッケージの管理
{
"name": "vba-formatter",
"version": "1.0.0",
"description": "VBA Code Formatter",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js",
"dev": "nodemon src/index.js"
},
"dependencies": {
"@aws-sdk/client-bedrock-runtime": "^3.712.0",
"dotenv": "^16.4.7",
"express": "^4.18.2",
"multer": "^1.4.5-lts.1"
},
"devDependencies": {
"nodemon": "^3.0.2"
}
}
require('dotenv').config();
const { BedrockRuntimeClient, InvokeModelCommand } = require("@aws-sdk/client-bedrock-runtime");
async function testBedrock() {
console.log("環境変数確認:");
console.log("Region:", process.env.AWS_REGION);
console.log("Access Key ID exists:", !!process.env.AWS_ACCESS_KEY_ID);
console.log("Secret Access Key exists:", !!process.env.AWS_SECRET_ACCESS_KEY);
const client = new BedrockRuntimeClient({
region: process.env.AWS_REGION || "us-east-1",
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
}
});
// モデルごとに適切なリクエスト本文を生成する関数
function getRequestBody(modelId) {
if (modelId.startsWith('amazon.titan')) {
return {
inputText: "Hello",
textGenerationConfig: {
maxTokenCount: 10,
temperature: 0.7,
topP: 1
}
};
} else if (modelId.startsWith('anthropic')) {
return {
anthropic_version: "bedrock-2023-05-31",
max_tokens: 1024,
messages: [
{
role: "user",
content: "Hello, Claude!"
}
]
};
}
}
// テストするモデルのリスト
const modelIds = [
// Titan Models(動作確認済み)
"amazon.titan-text-express-v1",
"amazon.titan-text-lite-v1",
// Mistral Models
"mistral.mistral-7b-instruct-v0:2",
"mistral.mistral-large-2402-v1:0",
"mistral.mistral-large-2407-v1:0",
"mistral.mistral-small-2402-v1:0",
"mistral.mixtral-8x7b-instruct-v0:1",
// Claude Models
"anthropic.claude-3-sonnet-20240229-v1:0", // Claude 3 Sonnet
"anthropic.claude-3-haiku-20240307-v1:0", // Claude 3 Haiku
"anthropic.claude-3-opus-20240229-v1:0", // Claude 3 Opus
"anthropic.claude-3-5-haiku-20241022-v1:0", // Claude 3.5 Haiku
"anthropic.claude-3-5-sonnet-20241022-v2:0",// Claude 3.5 Sonnet v2
"anthropic.claude-3-5-sonnet-20240620-v1:0",// Claude 3.5 Sonnet
"anthropic.claude-v2:1", // Claude 2.1
"anthropic.claude-v2", // Claude 2
"anthropic.claude-instant-v1" // Claude Instant
];
// 各モデルをテスト
for (const modelId of modelIds) {
console.log(`\nテスト: ${modelId}`);
try {
const command = new InvokeModelCommand({
modelId: modelId,
contentType: "application/json",
accept: "application/json",
body: JSON.stringify(getRequestBody(modelId))
});
const response = await client.send(command);
console.log(`✅ ${modelId} は利用可能です`);
// レスポンスの内容を表示
const responseBody = Buffer.from(response.body).toString('utf8');
console.log('応答:', responseBody);
} catch (error) {
console.log(`❌ ${modelId}: ${error.message}`);
}
}
}
testBedrock();
node test-connection.js
使用方法
- Webブラウザで http://localhost:3000 にアクセス
- テキストエリアにVBAコードを貼り付け、または.basファイルを選択
- 「Format」ボタンをクリック
- 右側のテキストエリアにフォーマット済みコードが表示
要件定義
### VBA Formatter with Amazon Bedrock プロジェクト説明
#### プロジェクト概要
このプロジェクトはJavaScript言語でNode.jsを利用し、ExpressというWebアプリケーションフレームワークで作成されています。VBAコードを改善するためにAmazon BedrockのAIを活用しています。
#### ファイル構成と役割
1. `src/public/index.html`
- Webブラウザで表示されるユーザーインターフェース
- VBAコードの入力とAI改善結果の表示を担当
2. `src/index.js`
- サーバープログラムのメインファイル
- ブラウザからのリクエストを処理
- AIとの連携を管理
3. `src/bedrock/prompts.js`
- AIへの指示内容(プロンプト)を定義
- コードの改善ルールを記述
4. `src/bedrock/analyzer.js`
- Amazon BedrockのAIと通信
- AIからの応答を処理
#### 処理の流れ
1. ブラウザでVBAコードを入力
2. サーバーがコードを受け取り
3. AIに分析を依頼
4. 改善されたコードが表示
#### 改善点
これまでAWSコンソールのAmazon Bedrockプレイグラウンドでチャット形式で行っていたAIとの対話を、使いやすいWebインターフェースで実現。以下が改善されました:
- コードの入力が簡単に
- AIへの指示内容が明確に表示
- 結果の表示が見やすく
- 複数のコードを連続して処理可能
#### 拡張性
- AIへの指示内容(プロンプト)を簡単に更新可能
- 新しい改善ルールの追加が容易
- チーム固有のコーディングルールも組み込み可能
#### 今後の展開
- より詳細なコード分析機能の追加
- チーム固有のルールの組み込み
- バッチ処理機能の追加
要確認
formatter.jsで生成A使用しない処理を使用できていない
改善項目
AWS SDK の追加活用
- CloudWatch Logs: AIの応答ログを収集・分析
- S3: VBAコードのバージョン管理や履歴保存
- DynamoDB: 変換履歴や頻出パターンの保存
UI
- 差分表示機能(基本的なnpmパッケージで実現可能)
vercelで公開
ExpressがNext.jsではないのにスムーズな理由は:
- Vercelは「Next.jsのためのプラットフォーム」ではなく
- 「モダンなWebアプリケーションのためのプラットフォーム」
- ExpressもNode.jsアプリケーションとして自然にサポート
- @vercel/nodeが複雑な変換を全て自動化
このため、ExpressアプリケーションでもNext.jsと同じようにスムーズにデプロイできるのです。
- @vercel/node
デプロイフロー
vercel.jsonの作成
vercel.jsonは、Vercelにデプロイする際の設定ファイルです。
{
"version": 2,
"builds": [
{
"src": "src/index.js",
"use": "@vercel/node"
},
{
"src": "src/public/**",
"use": "@vercel/static"
}
],
"routes": [
{
"src": "/prompts",
"dest": "/src/index.js"
},
{
"src": "/format",
"dest": "/src/index.js"
},
{
"src": "/api/(.*)",
"dest": "/src/index.js"
},
{
"src": "/(.+\\.[a-zA-Z]+)$",
"dest": "/src/public/$1"
},
{
"src": "/(.*)",
"dest": "/src/public/index.html"
}
]
}
node_modules
.env
.env.local
.DS_Store
GitHubにリポジトリを作成
vercel.com にGitHubアカウントでログイン
GitHubリポジトリをインポート
.envの内容を「Environment Variables」設定
Deployで公開完了
お名前.comでFTPソフトを使用する手順
お名前ドットコム管理画面
レンタルサーバーのコントロールパネルに入ります
「基本設定」→「ファイル管理」クリック
該当のドメインを選択→「はじめる」
設定情報で確認できる下記がFTPソフトで接続する際に必要です
- ユーザー名
- パスワード
- FTPソフト
FTPソフトでの接続
SFTPで接続できない場合FTPでする必要があります
「FTPサーバー」が「ホスト名」に該当します
PHPのサーバー変数を確認する方法
Webサーバー経由とターミナル実行時の$_SERVER
変数の違い
Webサーバー経由での実行で確認(アクセス)することが重要です
Webサーバー経由でアクセスすることで$_SERVER
にはHTTPリクエストの情報が含まれます
Webサーバー経由でアクセスする手順
- プロジェクト内にPHPファイルを作成
- ブラウザからアクセス
Docker Desktopで開発している場合
対象のコンテナを選択
「Exec」タブから、順番に以下のコマンドを実行
パーミッションの確認と必要に応じて変更
ls -l /var/www/html/server-info.php
chmod 644 /var/www/html/server-info.php
PHPファイルの作成
echo '<?php
header("Content-Type: application/json");
echo json_encode($_SERVER);' > /var/www/html/server-info.php
Webサーバー経由でアクセス
curl localhost/server-info.php
<注意点>
パスが/var/www/html/
でない場合は、プロジェクトの正しいパスに変更してください
【GTM】カスタムイベントをトリガーにして確実にCV計測
カスタムイベントとは:
- データレイヤーを通じてGTMに送信される独自のイベントです
- 通常のページビューやクリックなどの標準イベントとは異なり、開発者が任意のタイミングで発火できます
- JavaScriptを使って明示的にトリガーする必要があります
カスタムイベントの設定方法:
- Webサイトのコード側で、以下のようなDataLayer.pushを実装します:
dataLayer.push({
'event': 'customEventName', // イベント名
'eventCategory': 'category', // 任意のパラメータ
'eventAction': 'action', // 任意のパラメータ
// その他必要なデータ
});
- GTM側では:
- トリガーの新規作成で「カスタムイベント」を選択
- イベント名に、dataLayer.pushで指定した’event’の値(例:’customEventName’)を設定
発火のタイミング:
- フォーム送信完了時
- 動画の再生開始/終了時
- スクロール到達時
- モーダル表示時 など、サイトの要件に応じて任意のタイミングで発火させることができます
具体例:サンクスページでの計測(よくある課題)
フォーム送信 → サンクスページ遷移 → 計測
よくある課題
このような通常のフローだと、以下のリスクがあります:
- ページ遷移の途中でユーザーが離脱してしまう
- 通信エラーでサンクスページが表示されない
- ブラウザバックなどで誤カウント
- サンクスページが正しく読み込まれない
計測のタイミングを前倒し
- フォーム送信完了時に仮想的なページビューイベントを発火
- 実際のページ遷移ではなく、JavaScriptによって発火される
Google TagManagerの推奨プラクティスの1つより確実にコンバージョンを計測するための、一般的で信頼性の高い方法だと言えます。多くのウェブサイトで採用されている標準的なアプローチです。
フォーム送信 → 【ここで計測】 → サンクスページ遷移
dataLayer.push({
'event': 'virtual_pageview',
// 必要に応じて追加パラメータ
});
トリガーにカスタムイベントを設定
<トリガーの設定>
トリガーのタイプ | カスタムイベント |
イベント名 | virtual_pageview(✓正規表現一致を使用) |
このトリガーの発生場所 | すべてのカスタムイベント |
注意点
- 各広告媒体専用のCVタグを個別に紐付ける必要がある
- 例えば、LINE広告のCV計測をする場合は、LINE用のCVタグを別途このトリガーに紐付ける必要がある
- タグが紐付けられていない媒体は計測されない
本日のCV計測に関するMTGについて、参加をご相談させていただきたく存じます。
現在、以下の点について理解を深めたいと考えております: ・各広告媒体でのCV計測の仕組み ・各LPでのコンバージョン実装方法 ・virtual_pageviewの具体的な運用方法 まだ理解が不十分な部分もあり、MTGを通じて学ばせていただければと存じます。
Amazon Bedrockの開発環境構築とAPI活用ガイド
はじめに
Amazon Bedrockの活用方法は、コンソールのプレイグラウンドだけではありません。開発環境を構築し、外部APIと連携することで、より高度な機能の実装が可能になります。
従来のAmazon Bedrockのコンソール画面からの基本的な操作
AWSマネジメントコンソールにログイン
「Amazon Bedrock」を選択
「Model access」を選択 使用したいモデルの横にあるチェックボックスを選択
左側メニューから「Playground」を選択
開発環境の選択肢
1. ローカル開発環境
- 自分のPCに開発環境を構築
- 必要なツール:
- Node.js/Python
- AWS CLI
- コードエディタ(VS Codeなど)
- メリット:カスタマイズ性が高い
2. AWS Cloud9
- AWSが提供するクラウドベースのIDE
- AWSサービスとの統合が事前設定済み
- ブラウザから直接開発可能
3. AWS CloudShell
- AWSコンソールから直接利用可能なシェル環境
- AWS認証情報が事前設定済み
- 基本的なAWSツールが利用可能
AWS SDKについて
AWS SDKとは:
AWS Software Development Kit(SDK)は、AWSの各種サービスをプログラムから利用するためのライブラリ群です。AIに限らず、以下のようなAWSサービス全般を扱えます
AWS SDKの実行環境
- 実行環境の制約
- AWSコンソール(プレイグラウンド)では実行不可
- 独自の実行環境が必要
- 必要な実行環境の例
- ローカルPC開発環境
- AWS Cloud9
- EC2インスタンス
- AWS Lambda
- 環境構築に必要なもの
- AWS認証情報(アクセスキー)
- AWS SDKのインストール
- 開発言語の実行環境(Node.js, Pythonなど)
プログラムからの活用方法
AWS SDK for JavaScript (Node.js)
const { BedrockRuntimeClient } = require("@aws-sdk/client-bedrock-runtime");
AWS SDK for Python (Boto3)
import boto3
bedrock = boto3.client('bedrock-runtime')
必要な認証情報
開発環境からAWSサービスを利用するには、以下の認証情報が必要です:
- アクセスキーID
- シークレットアクセスキー
- リージョン設定
プレイグラウンドと開発環境の違い
プレイグラウンドの制限
- 基本的なモデル操作のみ
- 外部サービス連携不可
- ファイル操作の制限
開発環境のメリット
- 外部APIとの連携が可能
- カスタム機能の実装
- 自動化やバッチ処理
- 他のAWSサービスとの統合
まとめ
より高度なアプリケーション開発には、適切な開発環境の構築と、AWS SDKを活用した実装が推奨されます。プレイグラウンドは簡単な実験や検証には適していますが、本格的な開発には開発環境の構築をお勧めします。
Generative AI Use Cases JP (GenU) のセットアップ
環境準備
Node.jsのインストール
AWS CLIのインストール
AWS CLIとは
AWSのサービスをコマンドラインから操作するためのツール
インストール手順はAWS CLI公式インストーラーをダウンロード
AWSクレデンシャルの設定
GitHubからClone
Cloneしたディレクトリでnpm ciを実行
Bootstrap
デプロイ
表示されたUrlにアクセス
CORSエラー(Cross-Origin Resource Sharing Error)とは?「Access-Control-Allow-Origin」の設定
CORSエラーとは
CORSは「異なるオリジン(ドメイン、プロトコル、ポート)間でのリソース共有」を制御するセキュリティの仕組みです。
そのため異なるオリジン間でリソースをリクエストしようとする際にブラウザが制限をかけることにより、CORSエラーが発生します。
「Access-Control-Allow-Origin」の設定
異なるオリジン間のリクエストで制限をうけずにアクセスするためには、レスポンスヘッダーに「Access-Control-Allow-Origin」を指定することで可能になります。
プリフライトリクエストとは
異なるオリジン(ドメイン)へリクエストを送る前の「事前確認」
- 本来のリクエストを送る前に、そのリクエストが安全かどうかを確認します
- HTTPの
OPTIONS
メソッドを使用して送信されます
プリフライトリクエストはブラウザが自動的に行います。開発者が明示的にコードを書く必要はありません。
プリフライトが必要になるケース
- 特殊なヘッダーを使用する場合
application/json
などの特殊なContent-Typeを使用する場合- GET/POST/HEAD以外のメソッド(PUT、DELETEなど)を使用する場合
プリフライトリクエストの流れ
ブラウザがOPTIONS
リクエストを送信 サーバーがCORS設定を返答 許可された場合のみ、本来のリクエストを送信
開発者ツールでの確認方法
- ブラウザの開発者ツールを開く
- Networkタブで確認
- OPTIONSメソッドのリクエスト → これがプリフライトリクエスト
- その直後の本来のリクエスト
リクエストヘッダー:
- Origin: [送信元のドメイン]
- Access-Control-Request-Method: [使用予定のメソッド]
- Access-Control-Request-Headers: [使用予定のヘッダー]
レスポンスヘッダー:
- Access-Control-Allow-Origin: [許可されているドメイン]
- Access-Control-Allow-Methods: [許可されているメソッド]
- Access-Control-Allow-Headers: [許可されているヘッダー]
ローカルのNext.jsのプロジェクトからさくらインターネットのレンタルサーバーのphpのAPIにアクセスする場合
デジタルイラスト制作ツール徹底比較:Adobe Fresco vs Procreate vs CLIP STUDIO PAINT
はじめに
デジタルイラストの世界では、様々な制作ツールが存在します。今回は、主要な3つのソフトウェアの特徴と違いを詳しく見ていきましょう。
Adobe Fresco
主な特徴
- Adobe Creative Cloud との完璧な連携
- ライブブラシ機能による水彩やオイルの自然な表現
- ベクターとラスターの両方に対応
- プロフェッショナル向けの充実した機能
メリット
- Photoshopとの互換性が高い
- クラウド同期で作品管理が容易
- 実際の画材のような描き心地
デメリット
- 月額サブスクリプション制
- 学習曲線がやや急
Procreate
主な特徴
- iPad専用に最適化された直感的なインターフェース
- カスタムブラシの作成が可能
- アニメーション機能搭載
- 手頃な価格の買い切り制
メリット
- シンプルで使いやすい
- 優れたパフォーマンス
- 豊富なショートカット機能
デメリット
- iPad専用のため、他のデバイスでは使用不可
- レイヤー数に制限あり
CLIP STUDIO PAINT
主な特徴
- マンガ・イラスト制作に特化
- 充実した3D素材とポーズ集
- 多彩なブラシと効果
- クロスプラットフォーム対応
メリット
- 専門的な制作に適した豊富な機能
- アセットストアで素材の入手が容易
- 安定した動作性能
デメリット
- 初心者には機能が多すぎる可能性
- インターフェースがやや複雑
選び方のポイント
- 用途による選択
- マンガ制作 → CLIP STUDIO PAINT
- 一般イラスト → Procreate
- 商業デザイン → Adobe Fresco
- 使用デバイス
- iPadのみ → Procreate
- マルチデバイス → CLIP STUDIO PAINTやAdobe Fresco
- 予算
- 買い切り希望 → ProcreateやCLIP STUDIO PAINT
- サブスクOK → Adobe Fresco
まとめ
それぞれのツールに特徴があり、一概にどれが最高とは言えません。自分の用途や好みに合わせて選択することをお勧めします。初心者の方はProcreateから始めるのが良いでしょう。プロフェッショナルな用途ではCLIP STUDIO PAINTやAdobe Frescoが適しています。
このブログ記事はいかがでしょうか?各ソフトウェアの特徴や比較点について、さらに詳しく知りたい部分がございましたら、お申し付けください。
Amazon Bedrock Amazon Titan(AIモデル)を使用して、シンプルなチャットプログラムの作成、応用(テキスト校閲)
シンプルなチャットプログラムの作成
Node.jsのプロジェクトを初期化
npm init -y
@aws-sdk/client-bedrock-runtime
Bedrock Runtime API のリクエスト作成と送信するために@aws-sdk/client-bedrock-runtimeをインストール
npm install @aws-sdk/client-bedrock-runtime
- Node.js環境でAWS Bedrockサービスと通信するための公式ライブラリ(AWSが提供する公式SDK)
- 【開発環境の構築が容易】Node.jsのインストールだけで開発開始可能
- フロントエンド開発との親和性が高い、共通の言語(JavaScript/TypeScript)使用
1)Node.jsライブラリ(データの前処理や基本的な処理)
- Tesseract.js(OCR)
- Express(APIサーバー)
- Multer(ファイルアップロード)
- Sharp(画像処理)
2)プロンプト:AIに適切な指示を出す
App.jsを作成
// AWS SDKから必要な機能をインポート
// BedrockRuntimeClient: AWSのBedrockサービスに接続するためのクライアント
// InvokeModelCommand: AIモデルを呼び出すためのコマンド
const { BedrockRuntimeClient, InvokeModelCommand } = require('@aws-sdk/client-bedrock-runtime');
// AWS Bedrockクライアントの設定
// region: AWSのサービスを使用するリージョン(地域)
// credentials: AWS認証情報
const client = new BedrockRuntimeClient({
region: "us-east-1", // 米国東部(バージニア)リージョン
credentials: {
accessKeyId: "あなたのアクセスキー", // AWSアクセスキー
secretAccessKey: "あなたのシークレットキー" // AWSシークレットキー
}
});
// AIに質問するための関数
// async/await: 非同期処理を扱うための構文
async function askAI(question) {
try {
// AIモデルへのリクエスト内容を設定
const input = {
// 使用するAIモデルを指定(Amazon Titan)
modelId: "amazon.titan-text-express-v1",
// データの形式をJSON形式に指定
contentType: "application/json",
accept: "application/json",
// AIモデルへの具体的な指示内容
body: JSON.stringify({
// 質問文を設定
inputText: question,
// AIの応答設定
textGenerationConfig: {
maxTokenCount: 1000, // 最大応答長
temperature: 0.7 // 応答の創造性(0-1: 高いほど創造的)
}
})
};
// AIモデルを呼び出すコマンドを作成
const command = new InvokeModelCommand(input);
// コマンドを実行してAIからの応答を取得
const response = await client.send(command);
// AIからの応答(バイナリ)をテキストに変換
const responseBody = JSON.parse(new TextDecoder().decode(response.body));
// 応答を表示
console.log("回答:", responseBody.results[0].outputText);
} catch (error) {
// エラーが発生した場合の処理
console.error("エラーが発生しました:", error);
}
}
// プログラムのテスト実行
// ここの質問を変更することで、違う質問ができます
askAI("こんにちは!簡単な自己紹介をしてください。");
AWSが提供しているclient.send()
というメソッドが 内部でPromiseを使用していて 私たちはそれをawait
で待っている
AWSのアクセスキーとシークレットキーを取得する
IAMユーザーの作成
- 「ポリシーを直接アタッチする」を選択
- 検索ボックスに「Bedrock」と入力
- 「AmazonBedrockFullAccess」にチェック
- 「次へ」をクリック
- 作成したユーザー名をクリック
- 「セキュリティ認証情報」タブを選択
- 「アクセスキーを作成」ボタンをクリック
- 「ユースケース」で「サードパーティーのサービス」を選択
- 警告の確認チェックボックスにチェック
- 「次へ」をクリック
ファイルを実行
先ほど作成したapp.jsに取得したキーに更新してファイルを下記の通り実行すると、、
node app.js
回答:
私はAIです。人工知能の一種で、あなたの質問に答えたり、あなたの指示に従ったりすることができます。
私はあなたとチャットできることをうれしく思います。
テキスト校閲
使っているもの:
- AWS Bedrockのサービス
- Amazon TitanのAIモデル
- AWS SDKのクライアント
async function checkText(text) {
try {
// 1. AIへの指示(プロンプト)を作成
const input = {
modelId: "amazon.titan-text-express-v1", // Amazon TitanのAIモデル
contentType: "application/json",
accept: "application/json",
body: JSON.stringify({
// ここが重要:AIへの指示内容
inputText: `
以下の文章を校閲してください。
- 誤字脱字
- 文法の間違い
- より良い表現の提案
を箇条書きで指摘してください。
文章:${text}`,
textGenerationConfig: {
maxTokenCount: 1000,
temperature: 0.7
}
})
};
// 2. AIに送信して結果を待つ
const command = new InvokeModelCommand(input);
const response = await client.send(command);
// 3. 結果を表示
const responseBody = JSON.parse(new TextDecoder().decode(response.body));
console.log("校閲結果:", responseBody.results[0].outputText);
} catch (error) {
console.error("エラーが発生しました:", error);
}
}
上記のの校閲処理は特別な校閲ロジックは実装していないので、全てAIモデル(プロンプトの指示)に依存しています
より高度なプログラムにするために下記のようにします
- 独自の校閲ルール
- 形態素解析
- 文法チェックライブラリ などを組み合わせる
// 1. 独自の校閲ルールを追加
const customRules = {
"です/ます": "敬語の統一をチェック",
"こと/もの": "表現の統一をチェック",
// ... その他のルール
};
// 2. 形態素解析ライブラリの使用
// npm install kuromoji
const kuromoji = require('kuromoji');
// 3. 独自の校閲ロジックを実装
async function advancedCheck(text) {
// AIによる校閲
const aiResult = await checkText(text);
// 独自ルールによるチェック
const customResult = applyCustomRules(text);
// 形態素解析による確認
const morphResult = await morphologicalAnalysis(text);
return {
ai: aiResult,
custom: customResult,
morph: morphResult
};
}
独自の校閲ルール
// 独自の校閲ルールの実装例
class TextChecker {
constructor() {
// 基本的なルール定義
this.rules = {
// 表記ゆれチェック
styleRules: {
'とっても': 'とても',
'みたい': 'ような',
'すごく': '大変'
},
// 文末表現チェック
endingRules: {
'です・ます': /([てで]います|てます|です)(?![かが])/,
'である': /である(?![かが])/
},
// ビジネス文書チェック
businessRules: {
'不適切': ['思います', 'そうです', 'だと思います'],
'推奨': ['考えられます', '示唆されます', '推察されます']
}
};
}
// テキストをチェックするメソッド
checkText(text) {
const results = [];
// 表記ゆれチェック
for (const [incorrect, correct] of Object.entries(this.rules.styleRules)) {
if (text.includes(incorrect)) {
results.push(`「${incorrect}」は「${correct}」に修正することを推奨します`);
}
}
// その他のルールも同様にチェック
return results;
}
}
形態素解析
// npm install kuromoji
const kuromoji = require('kuromoji');
class MorphologicalAnalyzer {
async initialize() {
return new Promise((resolve, reject) => {
kuromoji.builder({ dicPath: 'node_modules/kuromoji/dict' })
.build((err, tokenizer) => {
if (err) {
reject(err);
} else {
this.tokenizer = tokenizer;
resolve();
}
});
});
}
analyze(text) {
const tokens = this.tokenizer.tokenize(text);
return this.processTokens(tokens);
}
processTokens(tokens) {
const analysis = {
wordTypes: {}, // 品詞の分布
conjugations: [], // 活用の問題
suggestions: [] // 改善提案
};
tokens.forEach(token => {
// 品詞の集計
const pos = token.pos;
analysis.wordTypes[pos] = (analysis.wordTypes[pos] || 0) + 1;
// 活用のチェック
if (token.pos === '動詞' && token.conjugated_form !== '基本形') {
analysis.conjugations.push({
word: token.surface_form,
basic: token.basic_form,
type: token.conjugated_form
});
}
});
return analysis;
}
}
文法チェックライブラリ
// npm install textlint textlint-rule-preset-japanese
const { TextLintEngine } = require('textlint');
class GrammarChecker {
constructor() {
this.engine = new TextLintEngine({
rules: {
// プリセットルールの使用
'preset-japanese': true,
// カスタムルールの追加
'max-ten': {
// 一文での「、」の数を制限
max: 3
},
'sentence-length': {
// 一文の長さを制限
max: 100
}
}
});
}
async check(text) {
try {
const results = await this.engine.executeOnText(text);
return this.formatResults(results);
} catch (error) {
console.error('文法チェックエラー:', error);
return [];
}
}
formatResults(results) {
return results[0].messages.map(message => ({
line: message.line,
column: message.column,
message: message.message,
ruleId: message.ruleId
}));
}
}
組み合わせた場合
class ComprehensiveTextChecker {
constructor() {
this.customChecker = new TextChecker();
this.morphAnalyzer = new MorphologicalAnalyzer();
this.grammarChecker = new GrammarChecker();
}
async initialize() {
await this.morphAnalyzer.initialize();
}
async checkText(text) {
const results = {
customRules: this.customChecker.checkText(text),
morphology: await this.morphAnalyzer.analyze(text),
grammar: await this.grammarChecker.check(text)
};
// AIの結果と組み合わせる
const aiResults = await checkText(text); // 先ほどのAI機能
results.ai = aiResults;
return this.summarizeResults(results);
}
summarizeResults(results) {
// 結果を整理して返す
return {
suggestions: [
...results.customRules,
...results.grammar.map(g => g.message)
],
analysis: {
wordTypes: results.morphology.wordTypes,
grammarIssues: results.grammar.length,
aiSuggestions: results.ai
}
};
}
}
使用例
async function main() {
const checker = new ComprehensiveTextChecker();
await checker.initialize();
const text = "私はとっても疲れたので、今日は早く帰りたいと思います。";
const results = await checker.checkText(text);
console.log(results);
}
補足)フォルダ構成
text-checker-project/
│
├── src/ # ソースコードのメインディレクトリ
│ ├── checkers/ # 各チェッカーの実装
│ │ ├── CustomChecker.js # 独自ルールチェッカー
│ │ ├── MorphAnalyzer.js # 形態素解析
│ │ ├── GrammarChecker.js # 文法チェッカー
│ │ └── AIChecker.js # AI利用のチェッカー
│ │
│ ├── rules/ # ルール定義
│ │ ├── styleRules.js # 表記ゆれルール
│ │ ├── grammarRules.js # 文法ルール
│ │ └── businessRules.js # ビジネス文書ルール
│ │
│ ├── utils/ # ユーティリティ関数
│ │ ├── formatter.js # 結果フォーマット
│ │ └── logger.js # ログ処理
│ │
│ └── index.js # メインのエントリーポイント
│
├── config/ # 設定ファイル
│ ├── default.json # デフォルト設定
│ └── textlint-rules.json # textlintルール設定
│
├── tests/ # テストファイル
│ ├── customChecker.test.js
│ ├── morphAnalyzer.test.js
│ └── grammarChecker.test.js
│
├── examples/ # 使用例
│ └── basic-usage.js
│
├── docs/ # ドキュメント
│ ├── API.md
│ └── RULES.md
│
├── node_modules/ # 依存パッケージ
├── package.json
├── package-lock.json
└── README.md
src/index.js
const ComprehensiveTextChecker = require('./checkers/ComprehensiveChecker');
const config = require('../config/default.json');
module.exports = ComprehensiveTextChecker;
src/checkers/CustomChecker.js
const styleRules = require('../rules/styleRules');
const businessRules = require('../rules/businessRules');
class CustomChecker {
constructor(config = {}) {
this.rules = {
style: styleRules,
business: businessRules,
...config.rules
};
}
checkText(text) {
// 実装
}
}
module.exports = CustomChecker;
src/rules/styleRules.js
module.exports = {
// 表記ゆれルール
corrections: {
'とっても': 'とても',
'みたい': 'ような'
},
// その他のルール
};
package.json
{
"name": "text-checker",
"version": "1.0.0",
"main": "src/index.js",
"scripts": {
"start": "node src/index.js",
"test": "jest",
"lint": "eslint src/"
},
"dependencies": {
"kuromoji": "^1.0.0",
"textlint": "^12.0.0",
"textlint-rule-preset-japanese": "^7.0.0",
"@aws-sdk/client-bedrock-runtime": "^3.0.0"
},
"devDependencies": {
"jest": "^27.0.0",
"eslint": "^8.0.0"
}
}
examples/basic-usage.js
const TextChecker = require('../src/index');
async function example() {
const checker = new TextChecker();
await checker.initialize();
const text = "私はとっても疲れたので、今日は早く帰りたいと思います。";
const results = await checker.checkText(text);
console.log(results);
}
example().catch(console.error);
【Laravel 】PHPUnit/Laravel
テストとはアプリケーションが正しく動作することを確認する自動チェックです
テスト
Laravel
- 自動なんで、人間がやらなくていい(コマンドで走らせる)
- レッドグリーンテスト
- テスト駆動開発(Test-Driven Development: TDD)テストファーストなプログラムの開発手法
- テストコードを書くのに開発と同じ時間がかかるので、工数的には膨らむ。
【Laravel】Sanctumでシンプルな認証
トークンベースの認証により、ステートレスなAPI通信が可能
シンプルなメモアプリのAPI
// routes/api.php
// ユーザー登録
Route::post('/register', function (Request $request) {
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
]);
$token = $user->createToken('auth-token')->plainTextToken;
return response()->json([
'token' => $token
]);
});
// ログイン
Route::post('/login', function (Request $request) {
$user = User::where('email', $request->email)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
return response()->json(['message' => 'Unauthorized'], 401);
}
$token = $user->createToken('auth-token')->plainTextToken;
return response()->json([
'token' => $token
]);
});
// routes/api.php
Route::middleware('auth:sanctum')->group(function () {
// メモの作成
Route::post('/memos', function (Request $request) {
$memo = $request->user()->memos()->create([
'title' => $request->title,
'content' => $request->content
]);
return response()->json($memo);
});
// メモの取得
Route::get('/memos', function (Request $request) {
$memos = $request->user()->memos;
return response()->json($memos);
});
});
// ログイン
async function login() {
const response = await fetch('api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email: 'user@example.com',
password: 'password123'
})
});
const data = await response.json();
// トークンを保存
localStorage.setItem('token', data.token);
}
// メモを作成
async function createMemo() {
const response = await fetch('api/memos', {
method: 'POST',
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: '新しいメモ',
content: 'メモの内容'
})
});
const memo = await response.json();
console.log('作成されたメモ:', memo);
}
Amazon Bedrockとは
Amazon Bedrockとは
Amazon Bedrockは、さまざまなAIモデルを簡単に利用できるようにするAWSのサービスです
料金
基本的な課金構造
- 入力トークン数と出力トークン数で別々に課金
- 1,000トークンあたりの料金で計算
- 実際の使用量のみ請求(最低利用額なし)
モデル | バリアント | 入力料金 (1Kトークン) |
出力料金 (1Kトークン) |
備考 |
---|---|---|---|---|
Claude (Anthropic) | Claude Instant | 0.80円 | 2.40円 | 最も手頃な選択肢 |
Claude (Anthropic) | Claude 3 Sonnet | 3.00円 | 15.00円 | バランスの取れたモデル |
Claude (Anthropic) | Claude 3 Opus | 15.00円 | 75.00円 | 最高性能モデル |
Llama 2 (Meta) | Llama 2 13B | 0.60円 | 0.80円 | オープンソースベース |
Titan (Amazon) | Titan Text Lite | 0.30円 | 0.40円 | 軽量な処理向け |
- 言語による違い
- 英語:1単語≒1-2トークン
- 日本語:1文字≒2-3トークン
- 記号や空白:0.2-0.5トークン程度
- 一般的な目安
- チャット1往復:100-300トークン
- メール1通:200-400トークン
- 技術文書1ページ:500-800トークン
コスト最適化のポイント
- 入力は簡潔にすることでコストを抑えられる
- システムプロンプトの再利用で入力コストを削減
- 出力制限を設定して想定外の高額請求を防止
ハンズオン
AWSマネジメントコンソール経由
マネジメントコンソールにログイン
リージョンをバージニア北部にします
→ 使用できるプレイグラウンドに影響
「Amazon Bedrock」を選択
Amazon Bedrock をマネジメントコンソールから利用するには、最初に Base Models(基盤モデル)へのアクセスを設定する必要があります。
「モデルアクセス」
使用するモデルのアクセスを設定
→ 「✅アクセスが付与されました」となります。
「プレイグラウンド」から「Chat/text」を選択
「モデルを選択」
チャットで回答してくれました
「イメージ」で画像生成も可能
「AWS SDK」を実行しAmazon Bedrock APIを呼び出す
CloudShellを起動
すぐに使える主要なAIモデル
- Anthropicの「Claude」(テキスト生成)
- Meta「Llama 2」(言語モデル)
- Stability AI「Stable Diffusion」(画像生成)
- AmazonのTitanモデル など
DifyとAmazon Bedrockの違い
サービスの性質
- Dify:
- オープンソースのAIアプリケーション開発プラットフォーム
- 自社でホストすることも可能
- プロンプトエンジニアリングに特化
- より小規模なプロジェクトに適している
- Amazon Bedrock:
- AWSの完全マネージドサービス
- エンタープライズ向けの機能が充実
- 大規模なスケーリングが可能
- AWSの他サービスとの連携が容易
主な用途
- Dify:
- チャットボットの作成
- プロンプトの管理と最適化
- シンプルなAIアプリケーションの開発
- プロトタイピング
- Amazon Bedrock:
- 大規模なAIサービスの展開
- 複数のAIモデルの統合
- エンタープライズアプリケーションの開発
- 高セキュリティな環境でのAI活用
料金体系
- Dify:
- オープンソースで基本無料
- 自身でホストする場合はインフラ費用のみ
- クラウドホスティング版は従量課金
- Amazon Bedrock:
- AWS従量課金制
- 利用するAIモデルごとに料金が異なる
- エンタープライズ向けの価格帯
【WordPress PHPエラー】Fatal error: Allowed memory size of xxx bytes exhausted
Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate 430080 bytes) in /home/xxxx/public_html/xxxxx.com/wp-includes/wp-db.php on line xxxx
WordPressで固定ページを編集しようと編集をクリックすると上記エラーが発生してしまいました。
PHPのメモリ不足が原因のようです
詳しく言うと、許可されているメモリサイズ(268435456 bytes = 256MB)を使い果たして、
さらに397312 bytes(約388KB)の追加メモリ割りあてようとしたら失敗したようです
おそらく原因は固定ページ内に大量の画像ブロックを配置しているから
メモリの上限
メモリの制限は複数の層で設定されています
- レンタルサーバー(PHP)の制限
- サーバーの
php.ini
ファイルで設定される制限 - サーバー全体のPHPスクリプトに適用される最大メモリ使用量
- レンタルサーバーの管理画面やFTPでphp.iniをアップロードすることで変更可能
- WordPressの制限
- WordPressの
wp-config.php
ファイルで設定される制限 - PHPの制限を超えて設定することはできない
- 例:PHPの制限が256MBの場合、WordPressで512MBと設定しても256MBまでしか使用できない
1)サーバー全体のPHPで設定されるメモリ制限
これが最も上位の制限となり、この値を超えることはできない
通常、レンタルサーバーの管理画面やサポートでのみ変更可能
メモリの確認方法
ルードディレクトリに下記の様なphpファイルを設置しアクセスし、memory_limitを確認できます
info.php
<?php phpinfo(); ?>
↓アクセスしてみるとメモリ上限が確認できます
php.iniで上限をふやせます
お名前ドットコムをレンタルサーバーで使用している場合は下記の記事を参考できます。
ご利用サーバーのFTPサーバーへご接続いただき、該当ドメインディレクトリ直下に
エラーの内容以上のメモリサイズを記述した「php.ini」ファイルを設置(アップロード)してください。
※既に「php.ini」ファイルを設置されている場合には、同ファイルを修正してください。
レンタルサーバー側からメモリの使用が集中してアクセス制限がかかることがあります
「ERR_CONNECTION_TIMED_OUT」エラーの解決方法
2)WordPress側のメモリ
- WP_MEMORY_LIMIT:通常時の制限(デフォルト40M)
- WP_MAX_MEMORY_LIMIT:管理画面での制限(デフォルト256M)
ワードプレスのメモリは管理画面のツールのサイトヘルスから確認できます
一時的にメモリ制限を大幅に引き上げる方法:
// wp-config.phpに追加
define('WP_MEMORY_LIMIT', '256M');
define('WP_MAX_MEMORY_LIMIT', '512M');
wp-config.php冒頭の<?phpの次の行に追記しました
↓サイトヘルスで変更が確認できました
対処法
今回のエラー
Fatal error: Allowed memory size of 268435456 bytes exhausted (tried to allocate…
はどちらのエラー??
Fatal error: [エラーの説明] in [ファイルのパス] on line [行番号]
はPHPのエラーメッセージの形式
WordPressメディアライブラリから画像データそのものを削除すると、固定ページ上のギャラリーブロックに関連付けられていた画像の参照が切れ、メモリ消費が減少するため、編集画面に遷移できる可能性が高い
ただし、以下の点に注意が必要です:
- メディアライブラリから画像を削除した場合:
- 固定ページ上のギャラリーブロックは残りますが、削除した画像は「見つかりません」や空の枠として表示される
- ブロックエディタで開くと、画像が欠落したギャラリーブロックとして表示される
- サイト上では画像が表示されなくなる
- 推奨される手順:
- まずサイトとデータベースのバックアップを取る
- メディアライブラリから、問題のページで使用している画像を一部削除
- 編集画面にアクセスできるようになったら、不要なギャラリーブロックを整理
- 必要な画像は最適化して再アップロード
- より安全な代替アプローチ:
- まず、wp-config.phpでメモリ制限を一時的に引き上げてから
- 画像の整理と最適化を行う方が、データの制御がしやすい
このアプローチで編集画面にアクセスできるようになったら、今後のために:
必要に応じてギャラリーの分割 を検討することをお勧めします。
画像サイズの最適化
1ページあたりの画像数の制限
ワードプレスのデバッグモードを有効にして詳細なエラー情報を確認:
// wp-config.phpに追加
define('WP_DEBUG', true);
define('WP_DEBUG_LOG', true);
遅延読み込み(Lazy Loading)
遅延読み込みとは: 画面に表示されている部分の画像だけを読み込み、見えていない部分の画像は後回しにする仕組みです。
// functions.php
function add_lazy_loading_to_admin() {
// 管理画面でのみ実行
if (is_admin()) {
// インラインでJavaScriptを追加
wp_add_inline_script('jquery', '
jQuery(document).ready(function($) {
// 画像を監視するための設定
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute("data-src");
}
}
});
});
// 編集画面内の画像を処理
$("#post-body img").each(function() {
const img = $(this);
const originalSrc = img.attr("src");
img.attr("data-src", originalSrc);
img.attr("src", "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
observer.observe(img[0]);
});
});
');
}
}
add_action('admin_enqueue_scripts', 'add_lazy_loading_to_admin');
リスクについて
リスクは比較的低いです。理由は:
- 編集画面のみの変更
- 公開サイトには影響なし
- 画像データ自体は変更しない
- 簡単に元に戻せる
- functions.phpから該当コードを削除するだけ
- エラーが起きても
- 最悪の場合、画像が表示されないだけ
- WordPressの基本機能は影響を受けない
筆者はこの方法ではメモリ数は抑えることはできませんでした…
データベースクエリとリビジョンを対策
データベースクエリとは:
- WordPressがデータベースにデータを要求する際の命令
- 例:
- 記事の内容を取得
- 画像情報の取得
- カスタムフィールドの取得
- 一つのページを表示するために、多数のクエリが実行される
- 各クエリの結果はメモリに保存される
リビジョンとは:
- 記事の変更履歴を保存する機能
- 保存する度に新しいバージョンが作られる
- デフォルトでは無制限に保存される
- 各バージョンがデータベースに保存され、編集画面で読み込まれる
データベースクエリの設定を最適化
// 必要な項目のみを取得するように変更
function optimize_post_queries() {
add_filter('posts_fields', function($fields) {
global $wpdb;
// 必要最小限のフィールドのみを指定
return "{$wpdb->posts}.ID, {$wpdb->posts}.post_title, {$wpdb->posts}.post_content";
});
}
add_action('init', 'optimize_post_queries');
リビジョンの最適化
リビジョンのデフォルト設定:
- デフォルトでは無制限(制限なし)
- 自動保存は60秒間隔
- 全てのリビジョンがデータベースに保存される
// wp-config.php に追加するだけ
define('WP_POST_REVISIONS', 5); // リビジョンを5個に制限
LaravelのバージョンLaravel 10とLaravel 11の主な違い
Laravelの開発環境をSailerを使って構築する方法
Docker Desktopを起動しておく
ターミナルでUbuntuをコマンドライン環境として選択する(理由:本番環境でLinuxを使う、Laravelの定番)
プロジェクト作成コマンド
curl -s "https://laravel.build/laravel-study?with=mysql,mailpit" | bash
Dockerを起動しているのに「Docker is not running.」となってしまったら、下記の通りDocker desktopの設定を変更してみてください
./vendor/bin/sail up -d
① curl:
- ウェブ上からデータをダウンロードするためのツール
- この場合、Laravel Sailのインストールスクリプトを取得している
② -s:
- silentモード(進行状況などを表示しない)
- ダウンロード時の詳細な情報を省略する設定
③ https://laravel.build/:
- Laravel Sailの自動セットアップスクリプトが置かれているURL
④ laravel-study:
- 作成するプロジェクトの名前
- この部分は自由に変更可能(例:my-app, blog-systemなど)
⑤ ?with=mysql,mailpit:
- 一緒にインストールする追加機能の指定
- mysql:データベース
- mailpit:メール送信のテスト環境
⑥ | bash:
- ダウンロードしたスクリプトをすぐに実行する指示
プロジェクト初期設定
config\app.phpファイル
'timezone' => env('APP_TIMEZONE', 'UTC'),
'locale' => env('APP_LOCALE', 'en'),
env関数
.env( 設定項目名, デフォルト値)
.envファイル
# APP_TIMEZONE=UTC
APP_TIMEZONE=Asia/Tokyo
APP_URL=http://localhost
# APP_LOCALE=en
APP_LOCALE=ja
コントローラーの使い方と目的
./vendor/bin/sail artisan make:controller UtilityController
さくらインターネットのデータベースにさくらのWebサーバ以外からAPI経由でアクセスする方法
さくらインターネットのWebサーバー外部から直接アクセスできない
直接MySQLへの接続はブロックされています(セキュリティ対策)
解決策:API経由の接続
ローカルNext.jsからアクセスする場合
さくらインターネットの公開ディレクトにusers-api.phpを配置
<?php
header('Content-Type: application/json');
$host = 'mysql80.xxxx.sakura.ne.jp';
$dbname = 'xxxx';
$user = 'xxxx';
$pass = 'xxxx';
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
$stmt = $pdo->query('SELECT id, email, name FROM users');
$users = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode([
'success' => true,
'data' => $users
]);
} catch(PDOException $e) {
echo json_encode([
'success' => false,
'error' => $e->getMessage()
]);
}
?>
Next.jsから上記APIを呼び出す
app/api/users/route.ts
import { NextResponse } from "next/server";
export async function GET() {
try {
const response = await fetch('https://あなたのドメイン/users-api.php');
const data = await response.json();
return NextResponse.json(data);
} catch (error) {
return NextResponse.json({
error: 'ユーザー情報の取得に失敗しました'
}, { status: 500 });
}
}
参考サイト
LaravelでMySQLを使用したシンプルな認証システム (さくらインターネットDB作成手順)
MVCのルーティングの流れ
処理の流れ:
- ブラウザからリクエスト:
- ユーザーがURLにアクセス
- Routeが処理を振り分け:
- URLに合わせて適切なControllerを呼び出し
- ModelでDBアクセス:
- 必要なデータを取得/保存
- Viewで画面を作成:
- HTMLを生成
- ブラウザに結果を返す:
- 作成した画面を表示
まずはデータベースを作成
さくらインターネットでMySQLの用意をします
(さくらインターネットのレンタルサーバーでは、標準でMySQLが提供されています)
さくらのコントロールパネルにログイン
「データベース」メニューを選択
「新規作成」をクリック
データベース名、ユーザー名(データベース同一)、パスワードを設定
新規作成したDBを選択し「設定」から「phpMyAdmin」をクリック
Laravelプロジェクトでの設定
.envファイルでデータベース接続の設定
.env
ファイルを以下のように設定します
DB_CONNECTION=mysql
DB_HOST=mysql○○.db.sakura.ne.jp # さくらインターネットから提供されるホスト名
DB_PORT=3306
DB_DATABASE=データベース名 # さくらで作成したDB名
DB_USERNAME=ユーザー名 # さくらで設定したユーザー名
DB_PASSWORD=パスワード # さくらで設定したパスワード
Tinker でのDB接続確認方法
Tinkerとは
Tinker は 対話型コマンドラインツールでphpの実行やデータベースの操作が可能です
Tinkerの起動
php artisan tinker
DB接続を内容を確認
# DB接続確認(以下のいずれかを実行)
DB::connection()->getPdo(); # 接続成功ならPDOオブジェクトが表示
DB::connection()->getDatabaseName(); # データベース名が表示
# 実際のデータを確認
DB::table('users')->get(); # usersテーブルの全データ表示
テストデータの作成
Usersテーブルのマイグレーションファイルでテーブルの構造を定義
マイグレーションファイルは「データベースの設計図」のようなものです。
データベースの構造を定義
database\migrations\0001_01_01_000000_create_users_table.phpを編集
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('nickname', 100);
$table->string('email')->unique();
$table->string('password');
$table->tinyInteger('locked_flg')->default(0);
$table->integer('error_count')->unsigned()->default(0);
$table->timestamps();
});
}
};
Laravelには「マイグレーション」という機能があり、データベースのテーブル構造やカラムをPHPコードで定義し、コマンドを実行することでデータベースに反映します。
php artisan migrate
User.phpを編集しユーザー関連のロジックを定義
app\Models\User.phpを編集
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
/** @use HasFactory<\Database\Factories\UserFactory> */
use HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'nickname',
'email',
'password',
'lcoked_flg',
'error_count',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
];
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
// protected function casts(): array
// {
// return [
// 'email_verified_at' => 'datetime',
// 'password' => 'hashed',
// ];
// }
}
UserFactory.phpでユーザーデータを作成
database\factories\UserFactory.phpを編集
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
* @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
*/
class UserFactory extends Factory
{
/**
* The current password being used by the factory.
*/
protected static ?string $password;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'nickname' => fake()->name(), // ランダムな名前を生成
'email' => fake()->unique()->safeEmail(), // ランダムでユニークなメールアドレスを生成
'password' => static::$password ??= Hash::make('password'), // パスワードをハッシュ化
];
}
}
Laravelのデータベースシーディング(seeding)機能を使ってテストデータを作成
database\seeders\DatabaseSeeder.phpを編集
<?php
namespace Database\Seeders;
use App\Models\User;
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
User::factory(10)->create();
// User::factory()->create([
// 'name' => 'Test User',
// 'email' => 'test@example.com',
// ]);
}
}
php artisan db:seed
コマンドは、データベースに初期データを挿入するためのコマンドです。
php artisan db:seed
ログイン画面の設定
auth/login.blade.phpを作成
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ログイン</title>
<!-- Tailwind CSSを使用 -->
@vite('resources/css/app.css')
</head>
<body class="bg-gray-100">
<div class="min-h-screen flex items-center justify-center">
<div class="bg-white p-8 rounded-lg shadow-md w-96">
<h1 class="text-2xl font-bold mb-8 text-center">ログイン</h1>
@if ($errors->any())
<div class="bg-red-100 text-red-700 p-4 mb-4 rounded">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form method="POST" action="{{ route('login') }}">
@csrf
<!-- メールアドレス -->
<div class="mb-4">
<label for="email" class="block text-gray-700 mb-2">メールアドレス</label>
<input
type="email"
name="email"
id="email"
value="{{ old('email') }}"
class="w-full p-2 border rounded focus:outline-none focus:border-blue-500"
required
autofocus
>
</div>
<!-- パスワード -->
<div class="mb-6">
<label for="password" class="block text-gray-700 mb-2">パスワード</label>
<input
type="password"
name="password"
id="password"
class="w-full p-2 border rounded focus:outline-none focus:border-blue-500"
required
>
</div>
<!-- ログインボタン -->
<div class="flex flex-col gap-4">
<button type="submit"
class="w-full py-2 px-4 bg-blue-600 text-white rounded hover:bg-blue-700 focus:outline-none">
ログイン
</button>
</div>
</form>
</div>
</div>
</body>
</html>
ログイン機能のルーティングを設定
routes/web.phpを編集
use App\Http\Controllers\Auth\LoginController;
// ログイン関連のルート
Route::middleware('guest')->group(function () {
// ログイン画面表示
Route::get('/login', [LoginController::class, 'showLoginForm'])
->name('login');
// ログイン処理
Route::post('/login', [LoginController::class, 'login']);
});
// ログアウト(認証済みユーザーのみ)
Route::middleware('auth')->group(function () {
// ダッシュボード表示
Route::get('/dashboard', function () {
return view('dashboard');
})->name('dashboard');
// ログアウト処理
Route::post('/logout', [LoginController::class, 'logout'])
->name('logout');
});
Cursor始め方
ダウンロード
ダウンロードしたインストーラを起動
「Autocomplete Preferences(自動補完設定)」に関する設定画面
- GH Copilot …従来のもの
- CursorのCopilot++(デフォルト) …より強力なバージョンのCopilot(デフォルト)
「Data Preferences(データ設定)」
- データ提供
- プライバシーモード
WinSCPのインストール方法
インストール
下記サイトより
https://winscp.net/eng/download.php
「DOWNLOAD WINSCP」をクリック
ダウンロードしたインストーラーを実行
自分のみの利用の為、「現在のユーザー用にインストール」
使用許諾を「許諾」
標準的なインストール
インターフェースの設定は後から変更可能です
表示 →環境設定
インストール
完了
接続方法
- プロトコル →SFTP(デフォルト)
- ホスト名
- ポート番号 →22(デフォルト)
- ユーザ名
- パスワード
ssh接続を初回実行時、下記の警告が出ます
承認すると、再度パスワード入力を求められますので、実施
ディレクトリ同期
ディレクトリ同期移動
有効化すると左右同じディレクトリ階層で表示されます
ファイルの編集でエディタをVScodeにしたい場合
任意のファイルを「右クリック」→「編集」→「設定」に進みます
エディタから外部エディタで参照でVSCodeのexeファイルを選択
追加されたCode(VSCode)を上にをクリックし一番上に移動します
隠しファイル表示する方法
メニューバーの歯車(環境設定)からパネルを選択し、隠しファイルを表示するを✓
参考サイト
WinSCPとは?インストール方法や使い方を解説する【初心者向け】
https://miyashimo-studio.jp/blog/detail/winscp-how-to-use/
DockerでApacheのSSL接続
こんにちは!今回は、Docker環境でのSSL接続設定について詳しく解説します。SSL(Secure Sockets Layer)は、インターネット上での通信を暗号化し、セキュリティを向上させる重要な技術です。Dockerを使用した開発環境でも、SSL接続を適切に設定することで、より本番環境に近い安全な環境を構築できます。
自己署名証明書を使用する場合
https://localhostとしてブラウザで確認すると警告がでます
警告ページで「詳細」や「続行」などのオプションを探します。 リスクを理解した上で、「Webサイトへ進む」などのオプションを選択します。 ブラウザは警告を表示しつつも、ページの表示を許可します。
注意点:この方法は開発環境でのみ推奨されます。
1. docker-compose.ymlの設定
まず、docker-compose.yml
ファイルでSSL接続用のポートを公開する必要があります。以下のように設定します:
services:
app:
ports:
- "0.0.0.0:8082:80"
- "443:443"
この設定により、ホストマシンのポート443がDockerコンテナ内のポート443にマッピングされます。これにより、外部からのHTTPS接続(ポート443)をコンテナ内のApacheサーバーで受け付けることができます。
2. Dockerfileでの設定
次に、Dockerfileで以下のようなSSL関連の設定を行います:
1)SSLモジュールの有効化:
RUN a2enmod ssl
2)デフォルトのSSLサイト設定の有効化:
RUN a2ensite default-ssl
3)自己署名SSL証明書の生成:
RUN echo '#!/bin/bash
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/ssl/private/apache-selfsigned.key \
-out /etc/ssl/certs/apache-selfsigned.crt \
-subj "/CN=localhost"
sed -i "s|SSLCertificateFile.*|SSLCertificateFile /etc/ssl/certs/apache-selfsigned.crt|" /etc/apache2/sites-available/default-ssl.conf
sed -i "s|SSLCertificateKeyFile.*|SSLCertificateKeyFile /etc/ssl/private/apache-selfsigned.key|" /etc/apache2/sites-available/default-ssl.conf' > /usr/local/bin/generate_ssl_cert.sh
RUN chmod +x /usr/local/bin/generate_ssl_cert.sh && /usr/local/bin/generate_ssl_cert.sh
DockerfileのRUN
コマンドがデフォルトでroot権限で実行されるため、追加の権限設定なしでopenssl
コマンドを実行できます。複雑な権限設定なしで、システムレベルの操作を容易に行えます。
openssl
コマンドを使用して自己署名証明書を生成しています。-x509
:自己署名証明書を生成することを指定します。-nodes
:秘密鍵をパスワードで保護しないことを指定します。-days 365
:証明書の有効期間を1年に設定します。-newkey rsa:2048
:2048ビットのRSA鍵を新しく生成します。-keyout
と-out
:鍵と証明書の出力先を指定します。-subj "/CN=localhost"
:証明書のサブジェクト(ここではCommon Name)を設定します。…証明書が保護するドメイン名または IP アドレスを指定します
- sed は “stream editor” の略で、テキストの変換や置換に使用
- 置換パターン:
s|旧パターン|新パターン|
の形式で、|
はデリミタ(区切り文字)として機能
- 置換パターン:
4)ポート443の公開:
EXPOSE 80 443
PHP環境にLet’s Encryptを統合する手順
ローカルDocker開発環境では難しい、、、?
DockerPHP開発環境構築:mkcertを使用したSSL接続の設定
mkcertを使用することで、自己署名証明書の警告なしに安全な開発環境を構築できます。
1. mkcert「エムケイサート」のインストールと使用
1)Windows PowerShell”を右クリックし、“管理者として実行”を選択します。
2)Chocolateyがインストールされていない場合は、まずChocolateyをインストール
Chocolateyは、Windowsのためのパッケージマネージャーです。
Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
3)mkcertをインストール:
choco install mkcert
インストールの確認
mkcert --version
4)ローカル認証局のインストール
mkcert -install
インストールの確認
mkcert -CAROOT
5)証明書の生成:
プロジェクトディレクトリに移動し、以下のコマンドを実行:
mkdir certs
cd certs
mkcert localhost 127.0.0.1 ::1
→ファイル生成
・証明書ファイル: [最初のドメイン名]+[追加エントリ数].pem
(公開鍵を含む)
・秘密鍵ファイル: [最初のドメイン名]+[追加エントリ数]-key.pem
6)各ファイルを編集
鍵ファイルを作成したら下記を実施します
- Apache設定ファイルでは、SSLエンジンを有効にし、証明書と秘密鍵のパスを正しく指定しています。
- Dockerfileでは、Apacheの SSL モジュールを有効化しています。
- docker-compose.ymlでは、443ポートをホストにマッピングし、必要なボリュームをマウントしています。
project_root/
│
├── local-mysite/
│ ├── certs/
│ │ ├── localhost+2.pem
│ │ └── localhost+2-key.pem
│ ├── env_vars/
│ │ └── localhost
│ ├── sites-available/
│ │ └── 000-default.conf
│ ├── .htaccess
│ ├── docker-compose.yml
│ ├── Dockerfile
│ ├── php.ini
│ └── ssl_cert_gen.sh
│
└── mysite/
└── (PHPアプリケーションファイル)
▼docker-compose.yml
version: "3.4"
services:
app:
# Dockerfileからイメージをビルド
build:
context: .
dockerfile: Dockerfile
# ボリュームマウントの設定
volumes:
# 環境変数ファイルをマウント
- ./env_vars:/var/www/env_vars
# PHPアプリケーションのソースコードをマウント
- ../0824_mysite:/var/www/html
# SSL証明書をマウント(読み取り専用)
- ./certs/localhost+2.pem:/etc/ssl/certs/localhost+2.pem:ro
# SSL秘密鍵をマウント(読み取り専用)
- ./certs/localhost+2-key.pem:/etc/ssl/private/localhost+2-key.pem:ro
# Apache設定ファイルをマウント
- ./sites-available:/etc/apache2/sites-available
# ポートマッピングの設定
ports:
# HTTP: ホストの8082ポートをコンテナの80ポートにマッピング
- "0.0.0.0:8082:80"
# HTTPS: ホストの443ポートをコンテナの443ポートにマッピング
- "443:443"
# 環境変数の設定(必要に応じて追加)
environment:
- APACHE_DOCUMENT_ROOT=/var/www/html
▼Dockerfile
# SSL対応PHP開発環境のDockerfile
# PHP 8.1とApacheをベースとした公式イメージを使用
FROM php:8.1-apache
# カスタムApache設定ファイルをコンテナ内の指定された場所にコピー
COPY ./sites-available/000-default.conf /etc/apache2/sites-available/000-default.conf
# PDO_MySQL拡張をインストールし、Apacheのrewrite、proxy、proxy_httpモジュールを有効化
RUN docker-php-ext-install pdo_mysql && a2enmod rewrite proxy proxy_http
# SSLモジュールを有効化
RUN a2enmod ssl
# デフォルトのSSLサイト設定を有効化
RUN a2ensite default-ssl
# 必要なパッケージをインストール
RUN apt-get update && apt-get install -y \
curl \
gnupg \
git \
unzip \
openssl \
# Node.jsのセットアップスクリプトを取得して実行
&& curl -sL https://deb.nodesource.com/setup_18.x | bash - \
# Node.jsをインストール
&& apt-get install -y nodejs \
# パッケージリストを削除してイメージサイズを削減
&& rm -rf /var/lib/apt/lists/*
# Composerをコンテナ内にコピー
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
# コンテナ内の作業ディレクトリを設定
WORKDIR /var/www/html
# Composerの設定
ENV COMPOSER_HOME /var/www/.composer
RUN mkdir -p $COMPOSER_HOME && chown -R www-data:www-data $COMPOSER_HOME
# Apache実行ユーザーの設定
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
# Apacheの設定を変更し、.htaccessが機能するようにAllowOverrideをAllに設定
RUN sed -i '/<Directory \/var\/www\/>/,/<\/Directory>/ s/AllowOverride None/AllowOverride All/' /etc/apache2/apache2.conf
# コンテナのポート80(HTTP)と443(HTTPS)を公開
EXPOSE 80 443
# .htaccessファイルをコンテナ内にコピー
COPY .htaccess /var/www/html/.htaccess
# .htaccessファイルの所有権と権限を設定
RUN chown www-data:www-data /var/www/html/.htaccess && chmod 644 /var/www/html/.htaccess
# カスタムphp.iniをコンテナ内にコピー
COPY ./php.ini /usr/local/etc/php/php.ini
# PHPエラーログファイルを作成し、適切な権限を設定
RUN touch /var/log/php_errors.log && chmod 666 /var/log/php_errors.log
# mkcertで生成した証明書をコンテナ内にコピー
COPY ./certs/localhost+2.pem /etc/ssl/certs/localhost.crt
COPY ./certs/localhost+2-key.pem /etc/ssl/private/localhost.key
# 証明書のパーミッションを適切に設定
RUN chmod 644 /etc/ssl/certs/localhost.crt && \
chmod 600 /etc/ssl/private/localhost.key
# Apacheをフォアグラウンドで実行
CMD ["apache2-foreground"]
▼000-default.conf
<VirtualHost *:80>
ServerName localhost
DocumentRoot /var/www/html
Redirect permanent / https://localhost/
# HTTPのアクセスログとエラーログ(必要に応じてコメントを外す)
# ErrorLog ${APACHE_LOG_DIR}/error.log
# CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
<VirtualHost *:443>
ServerName localhost
DocumentRoot /var/www/html
SSLEngine on
SSLCertificateFile /etc/ssl/certs/localhost+2.pem
SSLCertificateKeyFile /etc/ssl/private/localhost+2-key.pem
<Directory /var/www/html>
AllowOverride All
Require all granted
</Directory>
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
```
この設定は、HTTPからHTTPSへのリダイレクトと、SSL/TLS接続の基本的な設定を提供します。
mkcertとopenssl、Let’s Encryptを使用したSSL設定の比較
mkcertとopenssl、Let’s Encryptを使用したSSL設定の主な違いは、使いやすさと生成される証明書の性質にあります。
mkcertは開発環境に特化しており、簡単な操作で信頼されたSSL証明書を生成できます。一方、opensslはより汎用的で、詳細な設定が可能ですが、使用にはより深い知識が必要です。
開発環境では、mkcertの使用が推奨されます:
- セットアップが簡単で、チーム全体で一貫した環境を構築しやすい。
- ブラウザ警告がなく、よりスムーズなテストが可能。
- 開発者が証明書の詳細な管理に時間を割く必要がない。
一方、以下の場合はopensslの使用が適切です:
- 本番環境用の証明書生成。
- カスタムな証明書要件がある場合。
- 証明書生成プロセスの詳細な制御が必要な場合。
重要なポイントは、開発環境と本番環境で異なるアプローチを取ることが多いということです。開発ではmkcertの簡便性を活かし、本番ではopensslやLet’s Encryptなどを使用して、より厳格なセキュリティを確保することが一般的です。
セキュリティの考慮事項
開発環境でのmkcert使用
利点:
- 簡単にローカルでHTTPS環境を構築できる
- 開発者の生産性向上
- 本番環境に近い条件でのテストが可能
制限:
- ローカルマシンでのみ信頼される証明書
- 公的に認証された証明書ではない
mkcertはローカル環境に最適化されているため、本番環境特有の問題を見逃す可能性がある
一般的な本番環境要件:
- 信頼された認証局(CA)による証明書の使用
- 強力な暗号化アルゴリズムの採用
- 定期的な証明書の更新
- 適切なサーバー設定(TLSバージョン、暗号スイートなど)
「PHPMailer」使用手順、セキュリティ(機密情報設定ファイルは別配置等)、Docker開発から本番環境へアップロード
ローカルDocker環境でPHPMailerを使用する初心者向けの手順
1)プロジェクトディレクトリ
project-dir
├dockerfile
└docker-compose.yml
2)Dockerfile作成
Dockerfileについて
Dockerfileに記述された指示に従って、Dockerイメージ(設計図)を作成します
▽Dockerfile
# PHP 7.4 と Apache がインストールされた公式イメージを使用
FROM php:7.4-apache
# システムの更新とZIP関連のライブラリをインストール
# libzip-dev: ZIPファイルを扱うためのライブラリ
RUN apt-get update && apt-get install -y libzip-dev
# PHPにZIP拡張機能をインストール
# これにより、PHPでZIPファイルの操作が可能になります
RUN docker-php-ext-install zip
# Composerのインストール
# Composerは、PHPの依存関係管理ツールです
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
3)docker-compose.yml作成
version: '3'
services:
web:
build: .
ports:
- "8080:80"
volumes:
- ./src:/var/www/html
4)project-dirにて「docker-compose build
」「docker-compose up -d
」
docker-compose build
:- このコマンドは Dockerfile に基づいてイメージをビルドします。
docker-compose up -d
:- このコマンドはコンテナを起動し、バックグラウンドで実行します。
- Docker Compose ファイルで定義されたボリュームをマウントします。
※もし指定されたホスト側のディレクトリ(この場合はsrc
)が存在しない場合、Docker は自動的にそれを作成します。
「docker desktop」でContainer作成が確認できます
5)index.php send_mail.phpを作成
project_root/
├── Dockerfile
├── docker-compose.yml
└── src/
├── index.php
└── send_mail.php
▽index.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>PHPMailerテスト</title>
</head>
<body>
<h1>PHPMailerテスト</h1>
<form action="send_mail.php" method="post">
<label for="to">宛先:</label>
<input type="email" id="to" name="to" required><br><br>
<label for="subject">件名:</label>
<input type="text" id="subject" name="subject" required><br><br>
<label for="message">本文:</label><br>
<textarea id="message" name="message" rows="4" cols="50" required></textarea><br><br>
<input type="submit" value="送信">
</form>
</body>
</html>
▽send_mail.php
<?php
use PHPMailer\PHPMailer\PHPMailer;// PHPMailerライブラリの読み込み
use PHPMailer\PHPMailer\Exception;// PHPMailerライブラリの読み込み
require 'vendor/autoload.php';// PHPMailerライブラリの読み込み
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$mail = new PHPMailer(true); //PHPMailerのインスタンス作成(trueは例外を有効にする)
try {
//SMTPサーバー:Gmailの設定
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com'; // SMTPサーバーを指定
$mail->SMTPAuth = true;
$mail->Username = 'yourmail@gmail.com'; // SMTPユーザー
$mail->Password = 'your app pass'; // SMTPパスワード
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
$mail->Port = 465;
//送信元、送信先の設定
$mail->setFrom('yourmail@gmail.com', 'Mailer');
$mail->addAddress($_POST['to']);
//メール本文
$mail->isHTML(true);
$mail->Subject = $_POST['subject'];
$mail->Body = $_POST['message'];
$mail->send();
echo 'メッセージが送信されました';
} catch (Exception $e) {
echo "メッセージを送信できませんでした。Mailer Error: {$mail->ErrorInfo}";
}
}
6)コンテナ内でPHPMailerをインストールします:
docker-compose exec web composer require phpmailer/phpmailer
7)Googleアプリパスワード生成
Google→セキュリティ→2段階認証プロセス→アプリパスワードから設定
send_mail.phpを編集
注意)本番環境では、これらの設定を環境変数や別の設定ファイルに移動し、Gitなどのバージョン管理システムにコミットしないようにすることをおすすめします。
セキュリティ面
サニタイズについて
サニタイズ(sanitize)とは、ユーザーから入力されたデータを安全で使用可能な形式に変換することを指します。
セキュリティヘッダ
セキュリティを強化するために使用される特別なHTTPヘッダ
- header(“X-XSS-Protection: 1; mode=block”); //ブラウザの組み込みのXSS対策フィルターを有効にし、攻撃を検出したら、ページの読み込みをブロックします。
- header(“X-Frame-Options: SAMEORIGIN”); //ページを<frame>、<iframe>、<embed>、<object>で表示することを許可します。ただし、同じオリジンの場合のみ。(クリックジャッキング対策)
- header(“X-Content-Type-Options: nosniff”); //ブラウザがコンテンツタイプをスニッフィングしないようにします。
- header(“Referrer-Policy: strict-origin-when-cross-origin”); //クロスオリジンのリクエストに対しては、Referer ヘッダーにはリクエスト元のオリジンのみを含めます。
- header(“Content-Security-Policy: default-src ‘self’; script-src ‘self’ ‘unsafe-inline’ ‘unsafe-eval’; style-src ‘self’ ‘unsafe-inline’;”); //様々な攻撃(XSS、データ注入など)からサイトを保護します。
サニタイズ、セキュリティヘッダを導入のためPHPファイルを修正する
▽send_mail.php
<?php
// ob_start(); は、「出力を一時的に裏側で溜めておく」という命令
// もし途中でエラーメッセージが表示されたり、予期せぬ出力があったりすると、セキュリティヘッダーを設定できなくなるため
ob_start();
// セッションが開始されていない場合のみ、設定を変更してセッションを開始
if (session_status() == PHP_SESSION_NONE) { // セッション関連の設定
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1); // セッションを開始
session_start();
} else {
// セッションが既に開始されている場合は、そのまま続行
session_start();
}
// CSRFトークンがセッションに存在しない場合は生成
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// セキュリティヘッダーの設定
header("X-XSS-Protection: 1; mode=block");
header("X-Frame-Options: SAMEORIGIN");
header("X-Content-Type-Options: nosniff");
header("Referrer-Policy: strict-origin-when-cross-origin");
header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';");
use PHPMailer\PHPMailer\PHPMailer;// PHPMailerライブラリの読み込み
use PHPMailer\PHPMailer\Exception;// PHPMailerライブラリの読み込み
require 'vendor/autoload.php';// PHPMailerライブラリの読み込み
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// CSRFトークンの検証
if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
// エラーをログに記録
error_log("CSRF token mismatch. POST token: " . ($_POST['csrf_token'] ?? 'not set') . ", Session token: " . ($_SESSION['csrf_token'] ?? 'not set'));
die('セッションが期限切れか無効です。ページを更新して再度お試しください。');
}
// 入力のサニタイズとバリデーション
$to = filter_var($_POST['to'], FILTER_SANITIZE_EMAIL);
$subject = htmlspecialchars($_POST['subject'], ENT_QUOTES, 'UTF-8');
$message = htmlspecialchars($_POST['message'], ENT_QUOTES, 'UTF-8');
// メールアドレスの妥当性チェック
if (!filter_var($to, FILTER_VALIDATE_EMAIL)) {
die('無効なメールアドレスです。');
}
$mail = new PHPMailer(true); //PHPMailerのインスタンス作成(trueは例外を有効にする)
try {
//SMTPサーバー:Gmailの設定
$mail->isSMTP();
$mail->Host = 'smtp.gmail.com'; // SMTPサーバーを指定
$mail->SMTPAuth = true;
$mail->Username = 'yourmail@gmail.com'; // SMTPユーザー
$mail->Password = 'your app pass'; // SMTPパスワード
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
$mail->Port = 465;
//送信元、送信先の設定
$mail->setFrom('yourmail@gmail.com', 'Mailer');
$mail->addAddress($to);
//メール本文
$mail->Subject = $subject;
$mail->Body = $message;
$mail->AltBody = strip_tags($message); //HTMLタグを除去
$mail->send();
echo 'メッセージが送信されました';
} catch (Exception $e) {
// 詳細なエラー情報を隠し、ログに記録します。
error_log("メール送信エラー: " . $mail->ErrorInfo);
echo "メッセージを送信できませんでした。管理者にお問い合わせください。";
}
// 新しいCSRFトークンの生成
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
} else {
// POSTメソッド以外でアクセスされた場合の処理
die('不正なアクセスです。');
}
// 出力バッファリングを終了し、出力を送信
ob_end_flush();
▽index.php
<?php
session_start();
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
} // CSRFトークンがセッションに存在しない場合は生成
?>
機密情報を含む設定ファイルを作成
メリット
- セキュリティ向上:機密情報(パスワードなど)をGitリポジトリにコミットせずに済みます。
(.gitignoreに追加すると) - ポータビリティの向上:異なるサーバーや環境への移行が容易
1)プロジェクトのルートディレクトリに configフォルダその中にconfig.php
を作成します
project-dir\config\config.php
<?php
define('SMTP_HOST', getenv('SMTP_HOST') ?: 'smtp.example.com');
define('SMTP_USER', getenv('SMTP_USER') ?: 'user@example.com');
define('SMTP_PASS', getenv('SMTP_PASS') ?: 'password');
2)上記修正に伴いsend_mail.php、docker-compose.ymlも修正
▽send_mail.php
…
require 'vendor/autoload.php';// PHPMailerライブラリの読み込み
require_once __DIR__ . '/../config/config.php';
…
//SMTPサーバー:Gmailの設定
$mail->isSMTP();
$mail->Host = SMTP_HOST; // SMTPサーバーを指定
$mail->Username = SMTP_USER; // SMTPユーザー
$mail->Password = SMTP_PASS; // SMTPパスワード
$mail->SMTPAuth = true;
$mail->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
$mail->Port = 465;
▽docker-compose.yml
volumes:
- ./src:/var/www/html
- ./config:/var/www/config.php
3)2)の修正を行った後、Dockerコンテナを再ビルドして起動
docker-compose down
docker-compose build
docker-compose up -d
2)プロジェクトのルートディレクトリに .gitignore
ファイルを作成
▽.gitignore
/config/config.php
本番環境のさくらインターネットへアップロード
ローカルDocker開発環境、さくらインターネットの本番環境の両環境併用できるよう下記の通り修正
▽config.php
<?php
// 環境の判別
$is_local = ($_SERVER['SERVER_NAME'] == 'localhost' || $_SERVER['SERVER_ADDR'] == '127.0.0.1');
// デバッグモードの設定
define('DEBUG_MODE', $is_local); // ローカルではデバッグモードON、本番では OFF
// 本番環境で一時的にデバッグモードを有効化する場合
// define('DEBUG_MODE', true); // コメントを外して使用
if ($is_local) {
// ローカル環境(Docker)の設定
define('SMTP_HOST', getenv('SMTP_HOST') ?: 'smtp.gmail.com');
define('SMTP_USER', getenv('SMTP_USER') ?: 'xxxx@gmail.com');
define('SMTP_PASS', getenv('SMTP_PASS') ?: 'xxxx');
define('SMTP_PORT', 587);
define('SMTP_SECURE', 'tls');
} else {
// さくらインターネット環境の設定
define('SMTP_HOST', 'xxxx');
define('SMTP_USER', 'xxxx');
define('SMTP_PASS', 'xxxx');
define('SMTP_PORT', 587);
define('SMTP_SECURE', 'tls');
}
// サイトの URL
define('SITE_URL', $is_local ? 'http://localhost:8080' : 'xxxx');
さくらインターネットのメール情報の確認方法の参考サイト
下記のホスト、ユーザ名、パスワードについて
- define(‘SMTP_HOST’, ‘xxxx’);
- define(‘SMTP_USER’, ‘xxxx’);
- define(‘SMTP_PASS’, ‘xxxx’);
さくらのメールボックスを PHPMailer で SMTP + STARTTLS で送信する時の注意点https://qiita.com/ameyamashiro/items/c7283bd1ec5dd3146ef9
▽send_mail.php
<?php
// ob_start(); は、「出力を一時的に裏側で溜めておく」という命令
// もし途中でエラーメッセージが表示されたり、予期せぬ出力があったりすると、セキュリティヘッダーを設定できなくなるため
ob_start();
// 設定ファイルの読み込み
if (strpos(__DIR__, '/home/siennahare23') !== false) {
// さくらインターネット環境
require_once '/home/siennahare23/config/config.php';
} else {
// ローカル環境
require_once __DIR__ . '/../config/config.php';
}
// セッションが開始されていない場合のみ、設定を変更してセッションを開始
if (session_status() == PHP_SESSION_NONE) {
// セッション関連の設定
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_secure', 1);
// セッションを開始
session_start();
} else {
// セッションが既に開始されている場合は、そのまま続行
session_start();
}
// CSRFトークンがセッションに存在しない場合は生成
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
// セキュリティヘッダーの設定
header("X-XSS-Protection: 1; mode=block"); //ブラウザの組み込みのXSS対策フィルターを有効にし、攻撃を検出したら、ページの読み込みをブロックします。
header("X-Frame-Options: SAMEORIGIN"); //ページを<frame>、<iframe>、<embed>、<object>で表示することを許可します。ただし、同じオリジンの場合のみ。(クリックジャッキング対策)
header("X-Content-Type-Options: nosniff"); //ブラウザがコンテンツタイプをスニッフィングしないようにします。
header("Referrer-Policy: strict-origin-when-cross-origin"); //クロスオリジンのリクエストに対しては、Referer ヘッダーにはリクエスト元のオリジンのみを含めます。
header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';"); //様々な攻撃(XSS、データ注入など)からサイトを保護します。
use PHPMailer\PHPMailer\PHPMailer;// PHPMailerライブラリの読み込み
use PHPMailer\PHPMailer\Exception;// PHPMailerライブラリの読み込み
require 'vendor/autoload.php';// PHPMailerライブラリの読み込み
if ($_SERVER["REQUEST_METHOD"] == "POST") {
// CSRFトークンの検証
if (!isset($_POST['csrf_token']) || !hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
// エラーをログに記録
error_log("CSRF token mismatch. POST token: " . ($_POST['csrf_token'] ?? 'not set') . ", Session token: " . ($_SESSION['csrf_token'] ?? 'not set'));
die('セッションが期限切れか無効です。ページを更新して再度お試しください。');
}
// 入力のサニタイズとバリデーション
$to = filter_var($_POST['to'], FILTER_SANITIZE_EMAIL);
$subject = htmlspecialchars($_POST['subject'], ENT_QUOTES, 'UTF-8');
$message = htmlspecialchars($_POST['message'], ENT_QUOTES, 'UTF-8');
// メールアドレスの妥当性チェック
if (!filter_var($to, FILTER_VALIDATE_EMAIL)) {
die('無効なメールアドレスです。');
}
$mail = new PHPMailer(true); //PHPMailerのインスタンス作成(trueは例外を有効にする)
// デバッグモードの設定
// $mail->SMTPDebug = 3; // デバッグ出力を有効化
$mail->Debugoutput = 'html'; // デバッグ出力形式をHTMLに設定
try {
//SMTPサーバー:Gmailの設定
$mail->isSMTP();
$mail->Host = SMTP_HOST; // SMTPサーバーを指定
$mail->Username = SMTP_USER; // SMTPユーザー
$mail->Password = SMTP_PASS; // SMTPパスワード
$mail->SMTPAuth = true;
$mail->SMTPSecure = SMTP_SECURE;
$mail->Port = SMTP_PORT;
//送信元、送信先の設定
$mail->setFrom(SMTP_USER, 'Mailer');
$mail->addAddress($to);
//メール本文
$mail->isHTML(true);
$mail->Subject = $subject;
$mail->Body = $message;
$mail->AltBody = strip_tags($message); //HTMLタグを除去
$mail->send();
echo 'メッセージが送信されました';
} catch (Exception $e) {
if (DEBUG_MODE) {
echo "Message could not be sent. Mailer Error: {$mail->ErrorInfo}<br>";
echo "Detailed error: " . $e->getMessage();
} else {
echo "メッセージを送信できませんでした。管理者にお問い合わせください。";
}
error_log("メール送信エラー: " . $mail->ErrorInfo);
}
// 新しいCSRFトークンの生成
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
} else {
// POSTメソッド以外でアクセスされた場合の処理
die('不正なアクセスです。');
}
// 出力バッファリングを終了し、出力を送信
ob_end_flush();
アップロード
<ローカルのディレクトリ構成>
project_root/
│
├── src/
│ ├── index.php
│ ├── send_mail.php
│ └── vendor/ ...
├── config/
│ └── config.php
│
└── docker-compose.yml
<本番環境のディレクトリ構成>
/home/xxxx/www/xxxx/
│
├── contact/
│ │
│ ├── index.php
│ ├── send_mail.php
│ └── vendor/ ...
│
└── config/
└── config.php
注意)デバッグモードの無効化
実際のフォーム
【VSCode連携】「GitHub Copilot」使い方
「GitHub Copilot」の特徴
VSCodeと連携可能(その他エディターも)
「GitHub Copilot」の費用
GitHub Copilot Individual サブスクリプションは、月単位または年単位のサイクルで利用できます。
- 毎月の支払いサイクルを選択すると、カレンダー月ごとに 10 米国ドル が課金されます。
- 年単位の支払いサイクルを選択した場合、年間 100 米ドル が課金されます。
GitHub Copilot の課金
https://docs.github.com/ja/billing/managing-billing-for-github-copilot/about-billing-for-github-copilot
「GitHub Copilot」の始め方
1)プロフィールアイコンをクリック、メニューから「Settings」をクリック
2)「Copilot」をクリック、「Start free trial」をクリック
3)プランを選択し、「Get access to GitHub Copilot」をクリック
4)名前、住所を入力し、「Save」
5)クレジットカード等支払い方法情報を入力
6)登録情報を確認後「Submit」をクリック
Select Your preferencesで好みの設定
- 「Suggestions matching public code…」ではGithubのパブリックコードの使用を許可するかで、特にルールがなければ「Allowed」でOK
- 「Allow GitHub to use my code snippets from …」自分のコードをGithubが読み取ってよいかの設定です
※チェックしたとしてもプライベートコードは公開されません
以上で完了です
「GitHub Copilot」の解約方法
解約方法もGithubのページ、ユーザアイコンをクリックし設定より可能です
参考サイト
Rubyインストール手順(Windows)
Rubyインストーラーをダウンロード
RubyInstallerのダウンロードページ
https://rubyinstaller.org/downloads
PCのビット数を確認し、インストーラーを選択
▽設定→システム→バージョン情報より確認できます
インストーラーを実行
1)「Install for me only」をクリック
2)同意して「Next」をクリック
3)下記の設定(デフォルトのまま)「Install」をクリック
4)下記の設定(デフォルトのまま)「Next」をクリック
→インストールが開始されます
5)数分後、下記画面が表示されインストール終了
→「Finish」をクリックするとインストーラーの画面は消えてターミナルが表示されます
「MSYS2」のインストール
「MSYS2」… Ruby開発で便利なツール
「1」、「Enter」を押下するとインストールが開始されます
続いて「2」、「Enter」を押下、最新版にアップデート
最後に「3」、「Enter」を押下、3つ目のメニュー実行
完了したら「Enter」をクリックして「MSYS2」のインストール完了
Rubyインストールされているか確認
ターミナルにてバージョンを確認→表示されれば正しくインストールされています
C:\Users\xxx>ruby -v
ruby 3.3.3 (2024-06-12 revision f1c7b6f435) [x64-mingw-ucrt]
参考サイト
「Github Page」にReactを公開する方法
1)Reactプロジェクトを作成
npx create-react-app app-dir
2)Branchの発行し、GitHubのリポジトリを作成
VS Codeの場合「Branchの発行」をクリックしリポジトリ名を入力する
3)Reactアプリのビルド
npm run build
→buildディレクトリが作成されます
4)gh-pagesをインストール
Reactアプリを簡単にデプロイできる「gh-pages」ツールをインストール
npm install --save gh-pages
package.jsonを編集
▽package.jsonにて「homepageフィールド」の追加、「デプロイスクリプト」の追加
{
"homepage": "https://<GitHubアカウント名>.github.io/<GitHubリポジトリ名>/",
"name": "app-dir",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"predeploy": "npm run build",
"deploy": "gh-pages -d build",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
「npm run deploy」を実行する
cd app-dir
npm run deploy
公開URLについて
"homepage": "https://github0612.github.io/app-dir0612/",
「Branchの発行」後に作成されるリポジトリにてGitHubでBranchを「Save」をクリックすると数分後URLが表示されます
▽数分後URLが表示されます
▽サイトにて表示確認できます
参考サイト
GitHub Pages でReact Appを公開する方法
https://note.com/wecken/n/n73196eb22a51
ReactアプリをGithub Pagesにデプロイする方法
https://qiita.com/snow_swallow/items/8455dd135b81fe0ce25f
【WordPressエラー】Warning: Cannot modify header information – headers already sent by
エラー内容
functions.phpを作成し、編集していたところエラー発生。
「functions.php」の1行目のコメントアウトを削除したところ解消しました。
<!-- カスタムフィールドの値をタイトルに自動で反映 -->
<?php
add_action('acf/save_post', 'replace_post_title');
function replace_post_title( $post_id ) {
// タイトルの入ったフィールドを取得
$newtitle = get_field("news_text",$post_id);
//タイトルが空でない場合は更新
if( $newtitle ) {
$args = array(
"ID"=>$post_id,
"post_title" => $newtitle
);
wp_update_post($args);
}
}
▽1行目のコメントアウトを削除
<?php
add_action('acf/save_post', 'replace_post_title');
function replace_post_title( $post_id ) {
// タイトルの入ったフィールドを取得
$newtitle = get_field("news_text",$post_id);
//タイトルが空でない場合は更新
if( $newtitle ) {
$args = array(
"ID"=>$post_id,
"post_title" => $newtitle
);
wp_update_post($args);
}
}
参考サイト
【WordPressエラー】「Warning: Undefined variable $post in …」「Warning: Attempt to read property “ID” on null in …」
PHP7.3からPHP8.0に変更がありWordPressでエラーメッセージが発生しました。
表題のエラーがPHP8.0より通知ではなく、独立エラーとして扱われるようになった為です
エラー内容
「get_the_terms」記事に基づくタクソノミーの取得のための下記の記述でエラー発生
<?php
$terms = get_the_terms($post->ID, 'tag-workshop');
if ($terms) {
foreach ($terms as $term) {
echo '<a class="tagc-tag-workshop" tag-id="' . $term->term_id . '" href="' . get_permalink() . '">' . $term->name . '</a>';
}
?>
Warning: Undefined variable $post in …
Warning: Attempt to read property “ID” on null in …
(解決策)変数を定義する
Undefined variable $postとは変数の未定義によるエラーです
▽global $post;を追記することによりエラー解消しました
<?php
global $post;
$terms = get_the_terms($post->ID, 'tag-workshop');
if ($terms) {
foreach ($terms as $term) {
echo '<a class="tagc-tag-workshop" tag-id="' . $term->term_id . '" href="' . get_permalink() . '">' . $term->name . '</a>';
}
?>
参考サイト
【PHP7から8へ切替】Warning: Undefined variableが表示された場合の修正方法
https://it-column.mjeinc.co.jp/archives/3513
WordPressのグローバル変数「$post」とは?中身や使い方について解説
https://tcd-theme.com/2024/01/wp-post-object.html
PHP8.0でWordPressの「Attempt to read property “ID” on null」のエラーを解決したい
https://ja.stackoverflow.com/questions/85387/php8-0%e3%81%a7wordpress%e3%81%ae-attempt-to-read-property-id-on-null-%e3%81%ae%e3%82%a8%e3%83%a9%e3%83%bc%e3%82%92%e8%a7%a3%e6%b1%ba%e3%81%97%e3%81%9f%e3%81%84
【Googleタグマネージャー】「Page Path」で設定したが正しく発火しない
▽タグを作成したが発火しない
トリガーの設定は「Page Path」
トリガーの設定は「Page Path」で「含む」です。
パスはドメインの後ろの文字列です
「パス(/○○/)」の文字も間違いなく、サイトと一致しているのにもかかわらず発火しない
発火しない理由
「パス(/○○/)」に全角文字が含まれているからかと思われます。
▽WordPressパーマリンク設定で投稿名にチェックしているため、記事タイトルを日本語で書くと日本語パーマリンクになります
日本語パーマリンクの注意点
日本語パーマリンク(全角文字を含む)はコピーする際にエンコードされます。
例
(1. エンコード前)
https://internet.mints.ne.jp/googleタグマネージャーpage-pathで設定したが正しく発火し/
(2. 貼り付け後)
https://internet.mints.ne.jp/google%e3%82%bf%e3%82%b0%e3%83%9e%e3%83%8d%e3%83%bc%e3%82%b8%e3%83%a3%e3%83%bcpage-path%e3%81%a7%e8%a8%ad%e5%ae%9a%e3%81%97%e3%81%9f%e3%81%8c%e6%ad%a3%e3%81%97%e3%81%8f%e7%99%ba%e7%81%ab%e3%81%97
(解決策)トリガーの設定でエンコード後のパスを設定する
さいしょの発火しない設定方法は(1. エンコード前)のパスを設定していました。
パスを(2. 貼り付け後)…エンコードされたものを設定したところ正しく発火しました。
参考サイト
「Advanced Custom Fields」の内容を自動でタイトルに反映させたい
カスタムフィールドの値を自動でタイトルに反映させたい
▽投稿画面の編集項目をカスタムフィールドの値のみ(お知らせ内容のテキスト)にしています。
▽タイトルがないので、(タイトルなし)となります。
そこで「カスタムフィールドの値を自動でタイトルに反映させたい」ということになりました。
functions.phpを編集
// wordpressでタイトルを空白で投稿した時に、自動的にタイトルを挿入する
// https://teratail.com/questions/38598
add_action('acf/save_post', 'replace_post_title');
function replace_post_title( $post_id ) {
// タイトルの入ったフィールドを取得
$newtitle = get_field("news_text",$post_id);
//タイトルが空でない場合は更新
if( $newtitle ) {
$args = array(
"ID"=>$post_id,
"post_title" => $newtitle
);
wp_update_post($args);
}
}
※news_textはフィールド名です↓
参考サイト
WordPressでカスタムフィールドを自動保存したい
https://teratail.com/questions/38598
All-in-One WP Migration Unlimited Extensionにて「Unlimited Extension は最新バージョンではありません。使用前にプラグインを更新する必要があります。」
アップデート手順
1)「All-in-One WP Migration」をインストール、有効化
2)「all-in-one-wp-migration-unlimited-extension」をインストール
▽プラグインを有効化
3)バージョンを更新
▽「アップデートを確認」か「更新」をクリック
Maximum upload file size: Unlimited
さくらインターネットにて管理画面より「php.ini」を下記の通りに設定しおりますが、サイズ1Gのインポートもできました。
▽「php.ini」
→upload_max_filesize = 512M
▽ワードプレス「All-in-One WP Migration」インポート画面
→Maximum upload file size: Unlimited
【生成AI】【illustrator】テキストからベクター生成の使い方(バージョン28.0~新機能)
テキストからベクター生成の使い方
▽長方形ツールで表示させたい場所に作成
▽「ウィンドウ」→「プロパティ」をクリックするとプロパティ、「テキストからベクター生成(Beta)」が表示されます
▽プロンプトに生成したい画像の検索ワードを入力するとバリエーションが表示されます
似たようなテイストのイラストを作成する方法
▽「参照アセット」を有効化し「ピッカー」をクリックし似たテイストにしたい画像を選択→「生成(Beta)」をクリック
参考サイト
【イラレの生成AI】 テキストからベクター作成の使い方【Illustrator】
https://321web.link/illustrator-generative-vecter
【WordPress】本文入力欄を非表示にする方法
「カスタム投稿タイプ」、「カスタムフィールド」を利用しているときに、本文入力欄が不要な場合があると思います。
非表示にする方法の紹介です
functions.phpを編集する
// 本文入力欄非表示
add_action( 'init' , 'my_remove_post_support' );
function my_remove_post_support() {
remove_post_type_support('非表示にしたいカスタム投稿タイプスラッグ','editor');
}
【Googleサーチコンソール】検索画面で表示させたくない「Googleのインデックス削除」
Googleのインデックス削除とは
Googleのインデックス削除を申請すると検索画面で該当ページが表示されなくなります
不要なページのインデックス削除はSEOの観点からも重要
Googleサーチコンソールでインデックス削除の手順
▽削除したいURLのあるサイトのプロパティを選択し「削除」をクリック
▽「新しいリクエスト」クリック
▽URLの入力し「次へ」
▽「リクエストを送信」クリック
検索結果反映には1日ほどかかります
インデックス削除されているか確認する方法
site:該当URLで検索
参考サイト
【Googleサーチコンソール】プロパティタイプ「ドメイン」と「URLプレフィックス」違い
「ドメイン」と「URLプレフィックス」の2種類のプロパティタイプ
Googleサーチコンソールでプロパティを追加する場合「ドメイン」と「URLプレフィックス」の2種類のプロパティタイプがあります。
それぞれの特徴は下記の通り
プロパティタイプが「ドメイン」
- wwwあり/なし、http/https、サブドメイン、すべてのドメインを1つのプロパティで確認できる
- https://ntorelabo.com/(httpsでwwwあり)
- http://www.ntorelabo.com/(httpでwwwあり)
- https://ntorelabo.com/(httpsでwwwなし)
- http://ntorelabo/(httpでwwwなし)
- https://○○.ntorelabo/(サブドメイン)
- https://www.ntorelabo/○○/(サブディレクトリ)
- サブドメイン、サブディレクトリのみデータの確認ができない
- プロパティの追加方法がDNSレコードの確認のみ
プロパティタイプが「URLプレフィックス」
- 該当URLのみのため、wwwあり/なし、http/httpsの違いは対応できない
例)URLプレフィックス「https://ntorelabo.com/(httpsでwwwなし)」であれば、↓以下が対象- https://ntorelabo.com/(httpsでwwwなし)
- https://ntorelabo.com/〇〇〇〇/(httpsでwwwなし・サブディレクトリ)
- プロパティの追加方法が4種(ドメインより簡単)
- HTMLファイルのアップロード
- metaタグの追加
- Googleタグマネージャーのアカウントを利用
- Googleアナリティクスのアカウントを利用
【初心者】WordPressでjQuery使用するときの注意「Uncaught TypeError: $ is not a function」
Uncaught TypeError: $ is not a function
WordPressでjQueryを使用し「Uncaught TypeError: $ is not a function」と表示され、処理がされないとなりました。
そもそも「$(function(){})」とは
「$(function(){})」の記述によって中の処理はHTMLを読み込んでから実行されます
→この記述がないとHTMLの指定を含まれるjQueryのプログラムでエラーとなります
「Uncaught TypeError: $ is not a function」の原因
WordPressで「$」マークを使うとjQueryが動作しない場合があります。
※一般的に「jQuery」を省略し「$」と記述します
↓WordPressでデフォルトで読み込まれるjQueryに「noConflict」という関数があります、そのため$の使用ができません
「Uncaught TypeError: $ is not a function」の解決策
「$」が使用できないので、下記の通り書き直します
▼修正前
$(function(){
$('○○')○○;
});
▼修正後
jQuery(function() {
jQuery('○○')○○;
});
ネームサーバーをLOLIPOPに設定していて、DNSレコード設定がしたい場合(Googleサーチコンソールのプロパティの確認で必要)
Googleサーチコンソールでプロパティを追加する際にプロパティタイプをドメインを選択した場合、DNSレコードの確認が必要になります。(↓所有権の証明)
そこでDNSレコードの設定をすることになるのですが、LOLIPOPではできません。
ですが、LOLIPOPのサーバー契約のまま(サイトの表示等のサービスを利用)DNSレコードの設定をする方法がありますので、本記事投稿しました。
やや複雑な手順で、DNSレコードの意味や、各サーバー、ドメイン会社の特徴を理解しておく必要があります。
ネームサーバーをロリポップに設定している場合
今回該当パターンを確認すると下記2通り
- ロリポップでドメインを取得し、ロリポップのサービス(サイトの表示やメール)を利用
- 他社でドメインを取得し、ドメイン適用先でロリポップを選択
上記の場合ネームサーバーがロリポップに設定されます。
ロリポップではDNSレコードの設定ができない
ロリポップのネームサーバーを選択すると、DNSレコードの設定ができません。
ロリポップでネームサーバーを設定するとDNSレコードは下記の通りになりますが、
mytalk.site | IN | A | 163.44.185.218 | |
---|---|---|---|---|
mytalk.site | IN | MX | 10 | mx01.lolipop.jp |
mytalk.site | IN | NS | uns01.lolipop.jp | |
mytalk.site | IN | NS | uns02.lolipop.jp | |
mytalk.site | IN | TXT | v=spf1 include:_spf.lolipop.jp ~all |
ネームサーバー設定をすると、ネームサーバー以外のレコード値も自動的に設定されるようです。
お名前ドットコムで取得したドメインでロリポップにネームサーバー設定をしている場合、ロリポップのDNSを利用していることになります。この場合、お客様任意のレコード情報を設定することはできません。ロリポップを利用するために必要なレコードが自動で設定されております。
プライマリネームサーバー: uns01.lolipop.jp セカンダリネームサーバー: uns02.lolipop.jp
詳細は以下のリンクをご参照ください。
https://lolipop.jp/?a8=YRvtwRALNEc6Zvn0zhcfFz06io95nFHX4FPpFMoKusHcDFnKQEPR_EPL86uFSqvYuRQoNFP2tjZ5xs00000000404001
「ロリポップのDNS」はお客様任意のレコード情報を設定することができません
https://support.lolipop.jp/hc/ja/articles/4403084397203-%E3%83%AD%E3%83%AA%E3%83%9D%E3%83%83%E3%83%97%E3%81%AEDNS%E3%81%A7%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E6%83%85%E5%A0%B1%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B%E3%81%AB%E3%81%AF%E3%81%A9%E3%81%86%E3%81%97%E3%81%9F%E3%82%89%E3%81%84%E3%81%84%E3%81%A7%E3%81%99%E3%81%8B
DNSレコードの設定が可能なネームサーバーをロリポップ以外で設定する
DNSレコードの設定はしたいが、ロリポップのサービスは使用したい場合
DNSレコードの設定が可能な会社(ムームードメイン、Xserver …)にて下記の通り設定します
- Aレコード、MXレコードはそのまま
- ネームサーバーの値をロリポップから変更した先の値に変更
- TXTで追記したい内容を編集
(※1 記述方法はサーバー会社ごとに違うhttps://kiraba.jp/about-multi-spf-records-setting/)
(※2 _spf.lolipop.jpがないと正しくメールを使えない可能性がある(送信元ネームサーバーの認識、特定ができない→迷惑メールとされるかもしれない)
DNSレコード取得ツール
捕捉情報
Aレコードはサイト表示のため
MXレコードはメールを使うため
ネームサーバーの切り替えは(最大で)72時間
Xserverドメインで取得した独自ドメインをロリポップで使用
手順
▼Xserverレンタルサーバの管理画面にログインし対象のドメインを選択
▼ドメインの契約情報のネームサーバー設定の「設定変更」を選択
▼その他のサービスで利用するを選択しネームサーバー1、ネームサーバー2に下記の通り入力
ネームサーバー1 | uns01.lolipop.jp |
ネームサーバー2 | uns02.lolipop.jp |
確認画面へ進むをクリック
▼「設定を変更する」をクリック
▼ロリポップユーザー専用画面にて
公開(アップロード)フォルダについては複数のドメインを運用する場合は必ず入力
参考サイト
【LOLIPOP】サブディレクトリにインストールしたWordPressをドメイン直下で表示
インストール手順
<サーバーの管理画面>
WordPressのインストール
▼インストール先にwpディレクトリを指定
▼インストール履歴
<WordPress管理画面>
▼「/wp」を削除し、画面左下の「変更を保存」ボタンをクリック
※この変更に時点でドメイン直下に下記.htaccessが生成されました
(wpフォルダ内とは別でドメイン直下にも)
↓.htaccess(ドメイン直下に生成)
# BEGIN WordPress
# "BEGIN WordPress" から "END WordPress" までのディレクティブ (行) は
# 動的に生成され、WordPress フィルターによってのみ修正が可能です。
# これらのマーカー間にあるディレクティブへのいかなる変更も上書きされてしまいます。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
↓.htaccess(wpフォルダ内)
# BEGIN WordPress
# "BEGIN WordPress" から "END WordPress" までのディレクティブ (行) は
# 動的に生成され、WordPress フィルターによってのみ修正が可能です。
# これらのマーカー間にあるディレクティブへのいかなる変更も上書きされてしまいます。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /wp/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /wp/index.php [L]
</IfModule>
# END WordPress
↓こちらの手順はとばしてしまいました、、が問題なさそう。次回は実施したいと思います。
〇パーマリンク設定を保存する
設定 > パーマリンク へ移動し、何も変更せずに[変更を保存] をクリックします。
この操作で、WordPressに更新を伝えます。
<FTPソフト>
ファイル複製し編集
下記の通りのファイル構成です
〇wpフォルダ内index.phpファイルをダウンロード
参考サイトでは.htaccessの複製もしてましたが、
上記WordPress管理画面の設定でドメイン直下に生成されたので「.htaccess」についての手順を省きました
▼変更前
# BEGIN WordPress
# "BEGIN WordPress" から "END WordPress" までのディレクティブ (行) は
# 動的に生成され、WordPress フィルターによってのみ修正が可能です。
# これらのマーカー間にあるディレクティブへのいかなる変更も上書きされてしまいます。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /wp/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /wp/index.php [L]
</IfModule>
# END WordPress
▼変更後
# BEGIN WordPress
# "BEGIN WordPress" から "END WordPress" までのディレクティブ (行) は
# 動的に生成され、WordPress フィルターによってのみ修正が可能です。
# これらのマーカー間にあるディレクティブへのいかなる変更も上書きされてしまいます。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
〇wpフォルダ内のindex.phpをダウンロードし下記の通り編集
▼変更前
<?php
/**
* Front to the WordPress application. This file doesn't do anything, but loads
* wp-blog-header.php which does and tells WordPress to load the theme.
*
* @package WordPress
*/
/**
* Tells WordPress to load the WordPress theme and output it.
*
* @var bool
*/
define( 'WP_USE_THEMES', true );
/** Loads the WordPress Environment and Template */
require __DIR__ . '/wp-blog-header.php';
▼変更後
<?php
/**
* Front to the WordPress application. This file doesn't do anything, but loads
* wp-blog-header.php which does and tells WordPress to load the theme.
*
* @package WordPress
*/
/**
* Tells WordPress to load the WordPress theme and output it.
*
* @var bool
*/
define( 'WP_USE_THEMES', true );
/** Loads the WordPress Environment and Template */
require __DIR__ . '/wp/wp-blog-header.php';
ドメイン直下にアップロード
以上でドメイン直下でサイトの表示が確認できました
<補足>
サイト表示したときログインしているにもかかわらず、管理バーが表示されなかったのですが、WordPressをログインしなおしたら、表示されました。
複数のサブディレクトリにそれぞれWordPressサイトを作成し、ドメイン直下のindex.phpの記述によって切り替えができそうです
参考サイト
WordPressがドメイン直下ではなくサブディレクトリにインストールされている場合、「Override the base URL of the sitemap」を設定する必要があります。
https://nandemo-nobiru.com/wp-5941
【ロリポップ版】WordPressインストール方法・始め方
https://webst8.com/blog/lolipop-wordpress-open
ロリポップからWordPressを簡単インストールする方法を解説
https://communityserver.org/contents/4319
サブディレクトリ(/wp/)にインストールしたwordpressをドメイン直下に表示する方法【ルートディレクトリを変更】
http://kawatama.net/web/974#google_vignette
サブディレクトリにWordPressを作成した場合のサイトマップはどうすれば良いですか?
https://support.google.com/webmasters/thread/166288539/%E3%82%B5%E3%83%96%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%81%ABwordpress%E3%82%92%E4%BD%9C%E6%88%90%E3%81%97%E3%81%9F%E5%A0%B4%E5%90%88%E3%81%AE%E3%82%B5%E3%82%A4%E3%83%88%E3%83%9E%E3%83%83%E3%83%97%E3%81%AF%E3%81%A9%E3%81%86%E3%81%99%E3%82%8C%E3%81%B0%E8%89%AF%E3%81%84%E3%81%A7%E3%81%99%E3%81%8B%EF%BC%9F?hl=ja
LOLIPOPサーバーで「All-in-One WP Migration」のアップロードファイルサイズ変更方法
ロリポップサーバー ユーザー専用ページ
▼ユーザー専用ページの「サーバーの管理・設定」→「PHP設定」を選択
▼CGI版のPHPに変更
▼設定項目のupload_max_filesizeが「2M」、「2M」しか設定できません
そのため.htaccessに設定を追記する方法で行います
▼php_value,php_flagを利用可能にするを「On」に変更
.htaccessを編集
.htaccessの編集は「ダウンロード」を押して事前にバックアップをとっておくことをおすすめします
▼対象のドメイン.htaccessを下記の通り編集
以下の内容を追記
php_value memory_limit 512M
php_value upload_max_filesize 512M
php_value post_max_size 512M
# BEGIN WordPress
# "BEGIN WordPress" から "END WordPress" までのディレクティブ (行) は
# 動的に生成され、WordPress フィルターによってのみ修正が可能です。
# これらのマーカー間にあるディレクティブへのいかなる変更も上書きされてしまいます。
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
php_value memory_limit 512M
php_value upload_max_filesize 512M
php_value post_max_size 512M
▼WordPress管理画面からサイズ変更が確認できました
参考サイト
php_value memory_limit 128M
php_value upload_max_filesize 100M
php_value post_max_size 100M
ロリポップでWordPressの最大アップロードサイズを変更する方法
https://webst8.com/blog/lolipop-maximum-upload
php_value memory_limit 512M
php_value upload_max_filesize 512M
php_value post_max_size 512M
↓PHPをバージョン変更後、 WordPressが表示できなくなった
PHPのバージョン変更後、WordPressに関するエラーについて恐れ入りますがWordPress本体や各プラグインは弊社にて開発を行っていないため、ご利用のWordPress本体を含めテーマやプラグインが変更後のPHPのバージョンに対応しているか配布元に対応バージョンをご確認ください。
https://support.lolipop.jp/hc/ja/articles/360048375214-PHP%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E5%A4%89%E6%9B%B4%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E6%B0%97%E3%82%92%E3%81%A4%E3%81%91%E3%82%8B%E3%81%93%E3%81%A8%E3%81%AF%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%81%8B?a8=YRvtwRALNEc6Zvn0zhcfFz06io95nFHX4FPpFMoKusHcDFnKQEPR_EPL86uFSqvYuRQoNFP2tjZ5xs00000000404001
【お名前.com】Googleサーチコンソールで「所有権を証明できませんでした」の対応
起きていること
▼Googleサーチコンソールでプロパティを追加する際、下記の通り
対応方法
▼お名前ドットコムにログインしドメイン設定を選択
▼「DNS設定/転送設定」を選択
▼該当ドメイン選択後、次へクリック
▼DNSレコード設定を利用するの「設定する」をクリック
▼下記表のとおり各欄を入力後、追加をクリック
ホスト名 | TYPE | TTL | VALUE | 優先 | 状態 |
---|---|---|---|---|---|
空白 (所有権の証明を行うドメイン名) | TXT | 3600 | google-site-verification=********** (※Google管理コンソール内で取得した文字列) | なし | 有効 |
参考サイト
各レコードの意味は以下ご参照ください
https://help.onamae.com/answer/7883
【ドメイン】DNS設定の変更手続きをしてから有効になるまでの期間は?
https://help.onamae.com/answer/8081
【WordPress】パンくずリスト作成(プラグインBreadcrumb NavXT)
Breadcrumb NavXT
インストールして有効化
パンくずにホームページを含める。等設定をする
テンプレートファイル編集
ファイルの表示したい箇所に下記コード
<div class="breadcrumbs" typeof="BreadcrumbList" vocab="https://schema.org/">
<?php if(function_exists('bcn_display'))
{
bcn_display();
}?>
</div>
例)↓アクションフックでfunctions.phpに記述
add_action('main_hook', function () {
?>
<div class="breadcrumbs" typeof="BreadcrumbList" vocab="https://schema.org/">
<?php if(function_exists('bcn_display'))
{
bcn_display();
}?>
</div>
<style>
</style>
<?php
});
参考サイト
Breadcrumb NavXTプラグインの使い方(パンくずリストの表示)