スポンサーリンク

【ブログ】第110回 通販サイトを作ろう!

どうも、こんにちは。

「天下一品」を略すと「下品」になるのかな、と心配する緒方です。

ご機嫌いかがでしょうか。

常連さんとの会話中…

緒方「いらっしゃいませー。あ、お久しぶりです」

顧客「こんにちはー。実は最近私もハンドメイド始めたんだよね。だから浅草橋に来る機会が増えちゃって」

緒方「浅草橋は良い資材屋さん多いですからね」

顧客「そう。あと他の作品見て勉強しようと思って」

 

最近またハンドメイドを始めた人が増えたみたい。

コロナ禍でおうち時間が余った時にもハンドメイド人口が増えたけど、最近ではインフルエンサーの影響で、若い女の子の間で編み物がちょっとしたブームらしい。

たしかに、うちも最近布小物がよく売れてる気がする。

 

顧客「それでさminne登録してみたんだけど、私のなかなか見てもらえなくてねぇ…」

緒方「minneは登録者多いですもんね」

顧客「本当はネットでも販売したいんだけど、自分でホームページ作ったりするのは出来ないから。やっぱりデザフェスやマルシェにコツコツ出るしかないみたい」

 

なるほど、ネット通販は買い手側だけじゃなくて売り手側にも需要があるんだな。

うちも通販できたら良いんだけどな。ちょっとminneを調べてみるか。

 

 

おぉ、92万人か。いっぱいいるんだなぁ…。

 

トップページのデザインも見やすくて綺麗だなぁ。

そして作品がピックアップされて、その作品にアクセスが集中するわけか。

 

でもこれって、92万分の1でランダムにピックアップされてるとしたら

自分の作品が掲載される確率ってかなり低いな…。

何かしらのアルゴリズムでもあるんだろうか。

 

お客様が言ってた「なかなか見てもらえない」ってのはこの点だろう。

あと「写真を撮るのが苦手」とか「発送するのが大変」とか「見知らぬ人とのDMが怖い」とか言ってたな…。

 

そうか!これを全部うちで引き受ければいいんだな。

(ぼくのかんがえたさいきょうのサイト)

 

コミュニティを小規模にすれば自分の作品を見てもらえるし、発送や写真やクレームをこっちで全部引き受ければ既存の通販サイトと差別化できるから、面白いかもしれない。

ただ、一人でできる仕事量には限界があるから、プログラム化して自分自身の負担を減らす必要があるね。

 

よし!そうと決まったらminneを参考にして、ECサイトをハンドメイドしてみよう!

まずは上から検索窓をつけてみるか。

function.phpをいじって、ウィジェットに検索窓を出します。

//ヘッダーロゴの下に出力をする//
add_filter(‘wp_header_logo_after_open’, ‘add_header_contents’);
function add_header_contents() {
echo ‘<div class=”header-top-row”>’;

// 左側(検索ウィジェット)
echo ‘<div class=”header-search-box”>’;
dynamic_sidebar(‘add-header-contents’);
echo ‘</div>’;

}
// アナウンス枠エリアウィジェット
if (function_exists(‘register_sidebar’)) {
register_sidebar(array(
‘name’ => ‘ヘッダー領域追加コンテンツ’,
‘id’ => ‘add-header-contents’,
‘description’ => ‘ヘッダー領域に追加コンテンツを表示するウィジェットです。’,
‘before_widget’ => ‘<div class=”add-header-contents”>’,
‘after_widget’ => ‘</div>’,
));
}

 

次にjavascriptを設定。

$(‘.search-edit’).attr(‘placeholder’,’探し物はなんですか♪’);
function init() {
var px_change = 100;
window.addEventListener(‘scroll’, function(e){
if ( $(window).scrollTop() > px_change ) {
$(“#header-container”).addClass(“header-small”);
} else if ( $(“#header-container”).hasClass(“header-small”) ) {
$(“#header-container”).removeClass(“header-small”);
}
});
}
window.onload = init();

 

最後にcssでデザインの調整。

.header-top-row {
display: flex;
justify-content: space-between;
align-items: center;
gap: 1rem;
margin-top: 10px;
}

/* 左:検索フォーム */
.header-search-box {
flex: 1;
}

/* 検索フォームの位置と高さ調整 */
.search-box {
    width:35%;
    margin:0 1.5px 0.5em;
    position: relative; display: flex;
    left: 650px;
    bottom: 10px;
}
.search-edit {
  border-radius: 20px !important;
  border-color: #ffffff !important;
}
.search-submit{
  color: #a8bcc8;
}
input.search-edit {
  border: none;
  border-bottom: solid 1px #efefef;
  box-shadow: 0 4px 4px rgb(0 0 0 / 2%), 0 2px 3px -2px rgb(0 0 0 / 5%);
}
input.search-edit::placeholder {
  color: #ddd;
}
 /* 検索フォームヘッダー以外削除 */
#main .search-box.input-box {
  display: none;
}
/*768px以下*(スマホ用)/
@media screen and (max-width: 768px){
.search-box {
    width:70%;
    margin:0 1.5px 0.5em;
    position: relative; display: flex;
    left: 70px;
    bottom: 10px;
}
}
 /* ボタンの背景色 */
  border-bottom: solid 1px rgb(0 0 0 / 10%);
  border-radius: 0 3px 3px 0;
}

 

すると、

こんな感じ。

おぉ、ちょっとminneっぽくなった気がする!

 

次はスライド(カルーセル)かぁ。

うちはレンタルボックスだから、作品別のピックアップと棚ごとのピックアップが必要になるなぁ。

 

/************************************
** ヘッダー
************************************/
/* カルーセルカスタマイズ */
#carousel .a-wrap {
  padding-top: -10px;
  margin: initial;
  margin-top: -6px !important;
}
#carousel .carousel-entry-card-title {
  display: none;
}
#carousel .carousel-in {
  background-color: transparent;
}
#carousel .slick-dots {
  display: none !important;
}
.slick-slide img {
  border-radius: 10px;
  box-shadow: 0 2px 4px rgb(0 0 0 / 22%);
}
.carousel-entry-card-thumb {
  width: 100%;
}
.carousel .cat-label {
  display: none;
}
/*ヘッダー背景透明化*/
#header-container {
background-color: transparent;
}
/*グローバルメニューのフォントサイズ等変更*/
.navi-in .menu-header .item-label{
font-size: 18px;
}
/* ヘッダーメニューをホバーしたときに下部に中央から線を出す */
#navi .navi-in a:after{
  position: absolute;
  content: “”;
  left: 0%;
  bottom: 0px;
  height: 10px;
  width: 100%;
  background: #fcbc74;
  opacity: 0.5;
  transform: scale(0,1);
  transition: 0.3s;
}
#navi .navi-in a:hover:after{
  transform: scale(1);
}
/*ヘッダーメニューの枠線*/
nav#navi, .menu-header .sub-menu{
     border-bottom:1px solid;
border-left:1px solid;
     border-right:1px solid;
     border-top:1px solid;
border-color:#fcbc74
}
/*フロントページ上寄せ余白消し*/
.home main#main {
padding-top: 0;
}
.home .column-wrap.column-2 {
margin-top: 0;
}
.home .container .column-wrap > div {
padding-top: 0;
}

 

こんな感じかな。

スライドは少しゆっくりにしよう。

ついでに直近の納品には「New」が自動でつくようにしてみようかな。

 

////カルーセルを1枚ずつスライドさせる
//Slickの読み込み
if (!function_exists(‘wp_enqueue_slick’)) :
function wp_enqueue_slick() {
if (function_exists(‘is_carousel_visible’) && is_carousel_visible()) {
wp_enqueue_style(‘slick-theme-style’, get_template_directory_uri() . ‘/plugins/slick/slick-theme.css’);
wp_enqueue_script(‘slick-js’, get_template_directory_uri() . ‘/plugins/slick/slick.min.js’, array(‘jquery’), false, true);

$autoplay = (function_exists(‘is_carousel_autoplay_enable’) && is_carousel_autoplay_enable()) ? ‘autoplay: true,’ : ”;
$autoplaySpeed = (function_exists(‘get_carousel_autoplay_interval’)) ? intval(get_carousel_autoplay_interval()) * 1000 : 3000;

$data = ‘
(function($){
$(“.carousel-content”).slick({
dots: true,
‘ . $autoplay . ‘
autoplaySpeed: ‘ . $autoplaySpeed . ‘,
infinite: true,
slidesToShow: 5,
slidesToScroll: 1,
responsive: [
{ breakpoint: 1240, settings: { slidesToShow: 5, slidesToScroll: 1 } },
{ breakpoint: 1023, settings: { slidesToShow: 5, slidesToScroll: 1 } },
{ breakpoint: 834, settings: { slidesToShow: 2, slidesToScroll: 1 } },
{ breakpoint: 480, settings: { slidesToShow: 2, slidesToScroll: 1 } }
] });
})(jQuery);
‘;

wp_add_inline_script(‘slick-js’, $data, ‘after’);
}
}
add_action(‘wp_enqueue_scripts’, ‘wp_enqueue_slick’);
endif;
//サムネイルにNew
function add_new_mark_to_thumbnail($html, $post_id, $post_thumbnail_id, $size, $attr) {
// 投稿の公開日を取得
$post_date = get_the_time(‘U’, $post_id);
$now = current_time(‘timestamp’);

// 7日(7 * 24 * 60 * 60秒)以内なら「New!」を表示
$days = 5 * 24 * 60 * 60;
if (($now – $post_date) < $days) {
$html .= ‘<div class=”new-badge”>New!</div>’;
}
return $html;
}
add_filter(‘post_thumbnail_html’, ‘add_new_mark_to_thumbnail’, 10, 5);

 

 

おぉ!minne感出てきた!

でもトップページからアクセスした時、更新情報がないと見辛いよな。

よしそれも作ろう。

 

// 「What’s New」ショートコードを追加(通常投稿のみ対象)
function whats_new_generator() {
$args = array(
‘post_type’ => ‘post’,
‘posts_per_page’ => 10, // 更新状況を10件表示
‘orderby’ => ‘date’,
‘order’ => ‘DESC’,
);

$query = new WP_Query($args);

if (!$query->have_posts()) {
return ‘<p style=”color:red; text-align: center;”>エラー: 投稿が見つかりません。</p>’;
}

$output = ‘<div class=”whats-new-container”>’;
$output .= ‘<div class=”whats-new-title”>What\’s New!</div>’; // タイトルを枠の外に出す
$output .= ‘<div class=”whats-new-box”><div class=”whats-new-scroll”><ul class=”whats-new-list”>’;

while ($query->have_posts()) {
$query->the_post();
$formatted_date = date(‘Y-m-d’, get_post_time(‘U’, true)); // 確実に日付を取得

$output .= ‘<li><a href=”‘ . get_permalink() . ‘” class=”post-title”>’ . get_the_title() . ‘</a> <span class=”post-date”>’ . $formatted_date . ‘</span></li>’;
}

$output .= ‘</ul></div></div></div>’;
wp_reset_postdata();

return $output;
}
add_shortcode(‘whats_new’, ‘whats_new_generator’);

 

せっかくなのでデザインもうちのサイトっぽくcssで修正。

 

/************************************
** what’s new
************************************/
.whats-new-container {
text-align: center;
margin-bottom: 40px;
}
.whats-new-title {
font-size: 24px;
font-weight: bold;
color: #ff604b;
margin-bottom: 5px;
}
.whats-new-box {
width: 100%;
max-width: 600px;
margin: 0 auto;
border: 2px solid #ddd;
border-color: #ffc04c;
border-radius: 10px;
padding: 10px;
background: #f9f3ea;
text-align: center;
position: relative;
}
.whats-new-list {
list-style-type: none;
padding: 5px 0;
margin: 0;
}
.whats-new-list li {
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
padding: 5px 0;
font-size: 13px;
border-bottom: 1px dashed #bea375;

}
.whats-new-list li:last-child {
border-bottom: none;
}
.post-title {
text-decoration: none;
color: #6b3500;
font-size: 16px;
font-weight: bold;
}
.post-date {
color: gray;
font-size: 12px;
}
/* スクロール可能にする */
.whats-new-scroll {
max-height: 250px;
overflow-y: auto;
}

 

 

よし、良い感じだね。オリジナリティが出てきた。

あんまり凝りすぎるとキリがないから、この辺にしよう。

 

さて、本題のネットショップ機能に取り掛かろう。

ショップカート(買い物カゴ)を作るかなぁ。

phpMyAdminを使って情報をデータベース化して、カートに追加できるようにしよ。

 

<?php
/* Template Name: Cart Page */
session_start();
get_header();
if (!isset($_SESSION[‘cart’])) {
$_SESSION[‘cart’] = [];
}
// 別のデータベースに接続
$db_host = ‘x’;
$db_name = ‘x’;
$db_user = ‘x’;
$db_password = ‘x’;

try {
$dsn = “mysql:host=$db_host;dbname=$db_name;charset=utf8mb4”;
$pdo = new PDO($dsn, $db_user, $db_password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die(“データベース接続エラー: ” . $e->getMessage());
}
if ($_SERVER[“REQUEST_METHOD”] == “POST”) {
$product_id = isset($_POST[‘product_id’]) ? intval($_POST[‘product_id’]) : 0;
$quantity = isset($_POST[‘quantity’]) ? intval($_POST[‘quantity’]) : 1;
$price = isset($_POST[‘price’]) ? floatval($_POST[‘price’]) : 0.0;
$product_name = isset($_POST[‘product_name’]) ? htmlspecialchars($_POST[‘product_name’], ENT_QUOTES, ‘UTF-8’) : ”;

if ($product_id <= 0 || $quantity <= 0 || $quantity > 100) {
die(“不正な入力です。”);
}

$found = false;
foreach ($_SESSION[‘cart’] as &$item) {
if ($item[‘id’] == $product_id) {
$item[‘quantity’] += $quantity;
$found = true;
break;
}
}

if (!$found) {
$_SESSION[‘cart’][] = [
‘id’ => $product_id,
‘name’ => $product_name,
‘price’ => $price,
‘quantity’ => $quantity
];
}

header(“Location: /cart”);
exit();
}

// 商品削除処理(ID指定で削除)
if (isset($_GET[‘remove’])) {
$remove_id = intval($_GET[‘remove’]);
foreach ($_SESSION[‘cart’] as $key => $item) {
if ($item[‘id’] == $remove_id) {
unset($_SESSION[‘cart’][$key]);
break;
}
}
header(“Location: /cart”);
exit();
}

// カート表示
if (empty($_SESSION[‘cart’])) {
echo “<p>カートは空です。</p>”;
} else {
echo “<table border=’1′>”;
echo “<tr><th>商品名</th><th>価格</th><th>数量</th><th>小計</th><th>削除</th></tr>”;

$total = 0;

foreach ($_SESSION[‘cart’] as $key => $item) {
$subtotal = $item[‘price’] * $item[‘quantity’];
$total += $subtotal;

echo “<tr>”;
echo “<td>” . htmlspecialchars($item[‘name’], ENT_QUOTES, ‘UTF-8’) . “</td>”;
echo “<td>” . htmlspecialchars(number_format($item[‘price’]), ENT_QUOTES, ‘UTF-8’) . ” 円</td>”;
echo “<td>” . htmlspecialchars($item[‘quantity’], ENT_QUOTES, ‘UTF-8’) . “</td>”;
echo “<td>” . htmlspecialchars(number_format($subtotal), ENT_QUOTES, ‘UTF-8′) . ” 円</td>”;
echo “<td><a href=’?remove=” . htmlspecialchars($item[‘id’], ENT_QUOTES, ‘UTF-8’) . “‘>削除</a></td>”;
echo “</tr>”;
}

echo “</table>”;
echo “<p>合計金額: <strong>” . number_format($total) . ” 円</strong></p>”;
echo “<p><a href=’/’>買い物を続ける</a></p>”;
echo “<p><a href=’/checkout’>購入手続きへ</a></p>”;
}
get_footer(); ?>

 

んー、おかしいなぁ…。

カートページからなぜかリダイレクトしないんだよな。原因がわからん。

 

このあと2週間ほど色々試したのですが、結局解決しなかったのでショップカートは既存の無料プラグイン「WooCommerce」を使うことにした。

さて、普通のネットショップならこのまま使っても問題ないんだけど、うちはレンタルボックスだからサイト用に改造しなくてはならない。

 

というわけで、まずカスタム投稿用のプラグインをインストール。

 

そしてfunction.phpに追加で記入します。

 

//カスタム投稿product_cateのみアイキャッチから本文に表示
function add_thumbnail_before_content($content) {
    if (is_singular(‘product’) && has_term(”, ‘product_cate’)) {
        if (has_post_thumbnail()) {
            $thumbnail = ‘<div class=”custom-thumbnail center-thumbnail”>’ . get_the_post_thumbnail(null, ‘large’) . ‘</div>’;
            return $thumbnail . $content;
        }
    }
    return $content;
}
add_filter(‘the_content’, ‘add_thumbnail_before_content’);
//売切表示と出品リストの自動化
function get_works_by_product_cate($atts) {
    // ショートコードの属性を受け取る
    $atts = shortcode_atts(array(
        ‘category’          => ”, // 作品リスト(販売中)
        ‘category_contents’ => ”  // 作品紹介のコンテンツのみ
    ), $atts, ‘works_by_cate’);
    $output = ”;
    // 作品紹介のコンテンツのみ表示
    if (!empty($atts[‘category_contents’])) {
        $category_name = $atts[‘category_contents’];
        $content_query = new WP_Query(array(
            ‘post_type’      => ‘product’,
            ‘tax_query’      => array(
                array(
                    ‘taxonomy’ => ‘product_cate’,
                    ‘field’    => ‘name’,
                    ‘terms’    => $category_name,
                )
            ),
            ‘posts_per_page’ => -1,
            ‘orderby’        => ‘date’,
            ‘order’          => ‘DESC’,
            ‘post_status’    => ‘publish’
        ));
        if ($content_query->have_posts()) {
            $output .= ‘<h2 style=”text-align: center;”>作品紹介</h2>’;
            while ($content_query->have_posts()) {
                $content_query->the_post();
                // アイキャッチ画像取得
                $thumbnail = get_the_post_thumbnail(get_the_ID(), ‘full’, array(
                    ‘style’ => ‘display:block; margin:0 auto; width:100%; max-width:800px; height:auto;’
                ));
                $output .= ‘<div style=”border: 1px solid #ccc; padding: 10px; margin-bottom: 10px;”>’;
                $output .= ‘<h3 style=”text-align: center;”>’ . get_the_title() . ‘</h3>’;
                // アイキャッチをタイトルの下に表示
                if ($thumbnail) {
                    $output .= ‘<div style=”text-align: center;”>’ . $thumbnail . ‘</div>’;
                }
                // 本文(HTML適用)
                $output .= ‘<div>’ . apply_filters(‘the_content’, get_the_content()) . ‘</div>’;
                // 「詳細を見る」ボタンを追加
                $output .= ‘<div style=”text-align: center; margin-top: 10px;”>’;
                $output .= ‘<a href=”‘ . get_permalink() . ‘” class=”works-detail-button”>↑この作品を買いたいぜ!↑</a>’;
                $output .= ‘</div>’;
                $output .= ‘</div>’;
            }
        } else {
            $output .= ‘<p>品切れ中です…</p>’;
        }
        wp_reset_postdata();
        return $output; // 作品紹介のみ表示するので、ここで終了
    }
    // 作品リスト(販売中)
    if (!empty($atts[‘category’])) {
        $category_name = $atts[‘category’];
        $query = new WP_Query(array(
            ‘post_type’      => ‘product’,
            ‘tax_query’      => array(
                array(
                    ‘taxonomy’ => ‘product_cate’,
                    ‘field’    => ‘name’,
                    ‘terms’    => $category_name,
                )
            ),
            ‘posts_per_page’ => -1,
            ‘orderby’        => ‘date’,
            ‘order’          => ‘DESC’,
            ‘post_status’    => ‘publish’
        ));
        if ($query->have_posts()) {
            $output .= ‘<h2 style=”text-align: center;”>作品リスト</h2>’;
            $output .= ‘<table style=”width:100%; border-collapse: collapse;”>’;
            $output .= ‘<thead><tr>’;
            $output .= ‘<th style=”border: 1px solid #ccc; padding: 8px; text-align: center;”>作品名</th>’;
            $output .= ‘<th style=”border: 1px solid #ccc; padding: 8px; text-align: center; white-space: nowrap;”>税抜</th>’;
            $output .= ‘<th style=”border: 1px solid #ccc; padding: 8px; text-align: center; white-space: nowrap;”>税込</th>’;
            $output .= ‘<th style=”border: 1px solid #ccc; padding: 8px; text-align: center; white-space: nowrap;”>個数</th>’;
            $output .= ‘</tr></thead><tbody>’;
            while ($query->have_posts()) {
                $query->the_post();
                $price = get_post_meta(get_the_ID(), ‘price’, true);
$price_intax = get_post_meta(get_the_ID(), ‘price_intax’, true);
                $stock = get_post_meta(get_the_ID(), ‘stock’, true);
                $output .= ‘<tr>’;
                $output .= ‘<td style=”border: 1px solid #ccc; padding: 8px; text-align: left;”><a href=”‘ . get_permalink() . ‘”>’ . get_the_title() . ‘</a></td>’;
                $output .= ‘<td style=”border: 1px solid #ccc; padding: 8px; text-align: center; white-space: nowrap;”>’ . (!empty($price) ? esc_html($price) : ‘未設定’) . ‘</td>’;
$output .= ‘<td style=”border: 1px solid #ccc; padding: 8px; text-align: center; white-space: nowrap;”>’ . (!empty($price_intax) ? esc_html($price_intax) : ‘未設定’) . ‘</td>’;
                $output .= ‘<td style=”border: 1px solid #ccc; padding: 8px; text-align: center; white-space: nowrap;”>’ . (!empty($stock) ? $stock . ” : ‘未設定’) . ‘</td>’;
                $output .= ‘</tr>’;
            }
            $output .= ‘</tbody></table>’;
        } else {
            $output .= ‘<p>現在作品がありません。</p>’;
        }
        wp_reset_postdata();
    }
    // 売切一覧(非公開作品)をここに追加(作品リストのみに適用)
    $sold_query = new WP_Query(array(
        ‘post_type’      => ‘product’,
        ‘tax_query’      => array(
            array(
                ‘taxonomy’ => ‘product_cate’,
                ‘field’    => ‘name’,
                ‘terms’    => $category_name,
            )
        ),
        ‘posts_per_page’ => -1,
        ‘orderby’        => ‘date’,
        ‘order’          => ‘DESC’,
        ‘post_status’    => ‘private’
    ));
    if ($sold_query->have_posts()) {
        $output .= ‘<h2 style=”text-align: center;”>売切一覧</h2>’;
        $output .= ‘<table style=”width:100%; border-collapse: collapse;”>’;
        $output .= ‘<thead><tr>’;
        $output .= ‘<th style=”border: 1px solid #ccc; padding: 8px; text-align: center;”>作品名</th>’;
        $output .= ‘</tr></thead><tbody>’;
        while ($sold_query->have_posts()) {
            $sold_query->the_post();
            // 「非公開:」を削除した作品タイトル
            $title_clean = str_replace(‘非公開: ‘, ”, get_the_title());
            // アイキャッチ画像のURLを取得
            $thumbnail_url = get_the_post_thumbnail_url(get_the_ID(), ‘full’);
            $title_link = !empty($thumbnail_url)
                ? ‘<a href=”‘ . esc_url($thumbnail_url) . ‘” target=”_blank”>’ . esc_html($title_clean) . ‘</a>’
                : esc_html($title_clean);
            $output .= ‘<tr>’;
            $output .= ‘<td style=”border: 1px solid #ccc; padding: 8px; text-align: left;”>’ . $title_link . ‘</td>’;
            $output .= ‘</tr>’;
        }
        $output .= ‘</tbody></table>’;
    }
    wp_reset_postdata();
    return $output;
}
// ショートコードを作成
function works_by_cate_shortcode($atts) {
    return get_works_by_product_cate($atts);
}
add_shortcode(‘works_by_cate’, ‘works_by_cate_shortcode’);
// カスタムフィールドを追加
function add_product_meta_boxes() {
    add_meta_box(
        ‘product_meta_box’,           // メタボックスID
        ‘作品の詳細情報’,             // メタボックスタイトル
        ‘display_product_meta_box’,   // コールバック関数
        ‘product’,                    // 投稿タイプ(カスタム投稿)
        ‘normal’,
        ‘high’
    );
}
add_action(‘add_meta_boxes’, ‘add_product_meta_boxes’);
// メタボックスの内容
function display_product_meta_box($post) {
    // 既存のデータを取得
    $price = get_post_meta($post->ID, ‘price’, true);
$price_intax = get_post_meta($post->ID, ‘price_intax’, true);
    $stock = get_post_meta($post->ID, ‘stock’, true);
    // nonceフィールド(セキュリティ)
    wp_nonce_field(basename(__FILE__), ‘product_meta_box_nonce’);
    ?>
    <p>
<label for=”price”>税抜価格(数値または文字):</label><br>
        <input type=”text” id=”price” name=”price” value=”<?php echo esc_attr($price); ?>” style=”width:100%;”>
    </p>
    <p>
<label for=”price_intax”>税込価格(数値または文字):</label><br>
        <input type=”text” id=”price_intax” name=”price_intax” value=”<?php echo esc_attr($price_intax); ?>” style=”width:100%;”>
    </p>
    <p>
        <label for=”stock”>在庫数(数値または文字):</label><br>
        <input type=”text” id=”stock” name=”stock” value=”<?php echo esc_attr($stock); ?>” style=”width:100%;”>
    </p>
    <?php
}
// メタボックスの値を保存
function save_product_meta($post_id) {
    // nonceチェック
    if (!isset($_POST[‘product_meta_box_nonce’]) || !wp_verify_nonce($_POST[‘product_meta_box_nonce’], basename(__FILE__))) {
        return;
    }
    // 自動保存時は何もしない
    if (defined(‘DOING_AUTOSAVE’) && DOING_AUTOSAVE) {
        return;
    }
    // 権限チェック
    if (!current_user_can(‘edit_post’, $post_id)) {
        return;
    }
    // `price` の保存
    if (isset($_POST[‘price’])) {
        update_post_meta($post_id, ‘price’, sanitize_text_field($_POST[‘price’]));
    } else {
        delete_post_meta($post_id, ‘price’);
    }
// `price_intax` の保存
    if (isset($_POST[‘price_intax’])) {
        update_post_meta($post_id, ‘price_intax’, sanitize_text_field($_POST[‘price_intax’]));
    } else {
        delete_post_meta($post_id, ‘price_intax’);
    }
    // `stock` の保存(文字もOK)
    if (isset($_POST[‘stock’])) {
        update_post_meta($post_id, ‘stock’, sanitize_text_field($_POST[‘stock’]));
    } else {
        delete_post_meta($post_id, ‘stock’);
    }
}
add_action(‘save_post’, ‘save_product_meta’);
// 作品ページカスタマイズ
function change_related_products_text( $translated_text, $text, $domain ) {
    if ( $text === ‘Related products’ && $domain === ‘woocommerce’ ) {
        $translated_text = ‘▼こちらもおすすめ!▼’;
    }
    return $translated_text;
}
add_filter( ‘gettext’, ‘change_related_products_text’, 10, 3 );
// カスタムフィールドと標準価格に同数を自動入力
function sync_price_with_custom_field($post_id) {
    // 商品ページ以外では実行しない
    if (get_post_type($post_id) !== ‘product’) {
        return;
    }
    // price_intaxのカスタムフィールドの値を取得
    $custom_price = get_post_meta($post_id, ‘price_intax’, true);
    // 値が空でない場合、WooCommerceの価格フィールドに設定
    if (!empty($custom_price)) {
        update_post_meta($post_id, ‘_regular_price’, $custom_price); // 通常価格
        update_post_meta($post_id, ‘_price’, $custom_price); // 販売価格
    }
}
add_action(‘save_post’, ‘sync_price_with_custom_field’);
// 関連商品の「カートに追加」ボタンと商品名を非表示にする
function customize_woocommerce_related_products() {
    ?>
    <style>
        .related .product .button,
        .related .product h2.woocommerce-loop-product__title {
            display: none !important;
        }
    </style>

 

 

これで作家ページをブラウザから見るとこんな具合に。

(すみません。今ちょうど書いてる最中に売れたのでlilyさんのページを使わせてもらっています)

 

 

さらに、

 

 

作品個別ページではこんな感じに。

作家名はカテゴリ、ジャンルは作品タグとリンクするようになっています。

 

 

スクロールすると詳細情報。

そしてリストタブをクリックすると、

 

 

カスタムフィールドの情報がそのままテーブルに掲載されます。

 

 

そして、管理画面からこれを非公開にすると、

 

リストの売切一覧に移動するシステムになっています(このプログラムがバグばっかり出て本当に厄介だった)

 

さて、これを一つ一つ手入力すると時間がかかるので、

googleスプレッドシートからそのまま反映されるように設定します。

まずは納品書記入フォーマットタブを作ります。

 

 

関数を使ってデータがcsvインポート用のタブに反映するように設定します。

(このcsvデータはエアレジにそのまま転用できるので一石二鳥です!)

 

 

それぞれ見出しのカスタムフィールドと情報がリンクするように設定します。

 

 

で、このプラグイン「csvインポータ」をインストールして、

スプレッドシートの情報をアップ。すると、

 

 

こんな感じに情報をまとめてアップすることができるようになりました。

在庫情報もここで簡単に変更できるようになっています。

あとは細々した設定を。

 

// 作品ページカスタマイズ
function change_related_products_text( $translated_text, $text, $domain ) {
if ( $text === ‘Related products’ && $domain === ‘woocommerce’ ) {
$translated_text = ‘▼こちらもおすすめ!▼’;
}
return $translated_text;
}
add_filter( ‘gettext’, ‘change_related_products_text’, 10, 3 );

// カスタムフィールドと標準価格に同数を自動入力
function sync_price_with_custom_field($post_id) {
// 商品ページ以外では実行しない
if (get_post_type($post_id) !== ‘product’) {
return;
}

// price_intaxのカスタムフィールドの値を取得
$custom_price = get_post_meta($post_id, ‘price_intax’, true);

// 値が空でない場合、WooCommerceの価格フィールドに設定
if (!empty($custom_price)) {
update_post_meta($post_id, ‘_regular_price’, $custom_price); // 通常価格
update_post_meta($post_id, ‘_price’, $custom_price); // 販売価格
}
}
add_action(‘save_post’, ‘sync_price_with_custom_field’);

// 関連商品の「カートに追加」ボタンと商品名を非表示にする
function customize_woocommerce_related_products() {
?>
<style>
.related .product .button,
.related .product h2.woocommerce-loop-product__title {
display: none !important;
}
</style>
<?php
}

// 作品名のH2からH4へ変更
function change_product_title_tag() {
remove_action(‘woocommerce_shop_loop_item_title’, ‘woocommerce_template_loop_product_title’, 10);
add_action(‘woocommerce_shop_loop_item_title’, function() {
echo ‘<h4 class=”woocommerce-loop-product__title”>’ . get_the_title() . ‘</h4>’;
}, 10);
}
add_action(‘init’, ‘change_product_title_tag’);

// googleにリッチリザルトを認識させない
add_filter(‘woocommerce_structured_data_product’, ‘__return_empty_array’);
add_action(‘wp_head’, function() {
remove_action(‘wp_footer’, array(WC()->structured_data, ‘generate_json_ld’), 10);
}, 1);

 

 

このコードで閲覧者が好きそうな他の作品をおすすめします!

 

ちなみに

 

この赤丸のシェアボタンをクリックすれば、

 

 

ページごとシェアすることもできますよ。

ぜひ自分の作品や気に入った作品をシェアしてみてください!

さて、実際に買い物カゴに入れてみましょう。

 

 

作品に複数のタイプがある場合は、タイプを選択します。

 

 

下にスクロールすると追加情報が出てきますので、そこから番号を選んでくださいね。

 

 

買い物カゴに入れると他にも選んでいた作品の小計と、送料が含まれた合計が表示されます。

あとは購入手続きに進んでいただければ、買えちゃいます!!

 

「手数料や固定費がかからないレンタルボックス兼ECサイト」がようやくできました。

 

あー…大変だった。

でもこのシステムなら全部緒方一人で対応できそうです。

 

当店へ作品を納品してもらえればこちらでECサイトと連携しますので、ぜひお気軽にどうぞ!

もちろん写真撮影や郵送などの作家様へのお手間はありません。

納品楽しみにお待ちしてます!あと爆買いもお待ちしてますね!

タイトルとURLをコピーしました