人気の商品ほど早く売り切れ、売り切れた商品ほどコレクションの上のほうに居座る。在庫を持って販売しているネットショップなら、一度は目にする光景です。「ベストセラー」順で並べていれば、よく売れた商品が先頭に来るのは当然で、その商品がいま買えるかどうかは並び順に関係ありません。
この記事では、当社 UnReact が開発した Shopify アプリ「シンプル在庫切れ並び替え|売り切れ商品を末尾へ自動ソート」を、管理画面の画面と設定項目を 1 つずつ取り上げながら解説します。ボタンを押すと Shopify の管理画面のどこが変わるのか、ストアフロントでは何が起きて何が起きないのか、使い始める前に知っておきたい Shopify 側の制約は何か、といった運用の話まで含めています。
これから導入を考えている方は「メリット」「デメリット」「料金プラン」から、すでに入れている方は「各画面の説明」から読んでください。自分でコードを書いて同じことをしたい方向けに、テーマと Admin API での作り方も最後に載せています。
シンプル在庫切れ並び替えは、売り切れた商品をコレクションの最後尾へ自動で移動させる、並び替え専用のアプリです。対象にするコレクションは、マーチャントが 1 つずつ選びます。

このアプリは、ストアフロントに JavaScript を差し込んで見た目だけを入れ替える方式ではありません。Shopify の Admin API を使って、コレクションに登録されている商品の順番そのものを書き換えます。
そのため、テーマのコードには一切触れません。Dawn でも Horizon でも有料テーマでも同じように動き、テーマを乗り換えても設定をやり直す必要がありません。ストアフロントに読み込むファイルが 1 つも無いので、ページの表示速度にも影響しません。
並び替えのきっかけは、Shopify から届く通知です。
通知を受けた時点では「このコレクションは並べ直しが必要」という印を付けるだけで、実際の並べ替えは数分おきにまとめて行います。売れるたびに並べ替えると、同じコレクションへの書き換えが重なって順番が崩れるためです。注文が続いている間でも、印を付けてからまとめて 1 回並べ直すので、並びが行ったり来たりすることはありません。
末尾へ移した商品については、「もともと何番目にあったか」をコレクションのメタフィールドに記録しています。在庫が戻ったときに元の位置へ戻せるのは、この記録があるからです。
記録先は Shopify のメタフィールドだけで、外部のデータベースにはストアのデータを置いていません。記録しているのも、末尾へ移した商品とその位置、有効にする前の並び順の種類、最後に並べ替えた時刻といった、並べ替えに必要な最小限の情報です。
コレクションの最初の 1〜2 段は、お客様がもっともよく見る場所です。そこに Sold out の商品が並んでいると、お客様はスクロールしてからでないと買える商品に出会えません。スマートフォンでは 1 画面に 2〜4 商品しか出ないことも多く、売り切れが数件あるだけで最初の画面がまるごと買えない商品になります。
このアプリを有効にしておけば、売り切れた商品は自動で後ろへ下がり、先頭は常に在庫のある商品で埋まります。
売り切れ以外の商品の相対的な順番は、まったく変えません。在庫数の多い順や商品名の順に組み替えるようなことはしないので、マーチャントが意図して作った並び(季節のおすすめを先頭にする、価格帯ごとにまとめる等)がそのまま残ります。
在庫が戻った商品は記録しておいた元の位置へ戻り、あとから追加した商品は売り切れ商品の手前に差し込まれます。手動の並び順では新しい商品が最後尾に付くのが Shopify の決まりなので、何もしないと「新作が売り切れ商品の下に埋もれる」ことになりますが、それも自動で解消されます。
コレクションごとに「無効にする」を押せば、末尾へ移した商品を元の位置に戻し、並び順の種類も有効にする前のもの(ベストセラー、関連性が高い順など)へ戻します。試しに 1 つのコレクションだけ有効にしてみて、合わなければ戻す、という使い方ができます。
導入してから困らないように、できないことも先に書いておきます。どれも Shopify の仕組みに由来するもので、ほかのアプリでも事情は同じです。
プランは 1 つで、機能の制限はありません。
有効にできるコレクションの数、コレクションに入れられる商品の数に上限は設けていません。プランの詳細は Shopify App Store の掲載ページで確認できます。
ほかの Shopify アプリと同じ手順で入れられます。



このアプリが求める権限は、商品とコレクションの読み取りと書き込みだけです。顧客や注文のデータには触れません。インストール後は、Shopify の「設定」→「アプリ」からアプリの詳細を開くと、アクセスしている範囲を確かめられます。

インストールしただけでは、どのコレクションも並び替わりません。次の「各画面の説明」で、有効にするまでの流れを順に見ていきます。
管理画面の左のメニューには、3 つの画面が並びます。
使い始めるまでの流れは、「設定で判定の方法を確かめる(そのままでもよい)→ コレクション一覧で有効にする」の 2 手順だけです。


ここからが本題です。画面と、その中のボタン・設定項目を 1 つずつ見ていきます。
コレクション一覧の上には、「このアプリでできること・できないこと」という青い枠の案内が常に出ています。内容は、先に書いたデメリットと同じ 4 点です。

この案内はスタッフ全員が目にする場所にあるので、「並び替えを有効にしたのに検索結果の順番が変わらない」「お客様が価格順を選んだら売り切れが上に来た」といった問い合わせが社内で出たときの説明にも使えます。どちらも故障ではなく、Shopify の仕様です。
案内の下には、ストアのコレクションが表で並びます。列は左から次の 6 つです。

「末尾へ移動」の数字は、いま売り切れとして扱っている商品の数でもあります。在庫が戻れば減り、売り切れが増えれば増えます。ストアに 1 つもコレクションが無い場合は「コレクションがありません」と表示されるので、Shopify の管理画面でコレクションを作ってから戻ってきてください。
自動コレクション(条件で商品が決まるコレクション)も、手動で商品を選んだコレクションも、どちらも有効にできます。
無効のコレクションで「有効にする」を押すと、確認画面が開きます。ここでは、並び順の種類が手動に変わることと、元に戻したいときは「無効にする」を使うことが説明されています。

確認画面でもう一度「有効にする」を押すと、次の順で処理が進みます。
つまり、有効にする前に「ベストセラー」で並べていたコレクションは、その瞬間のベストセラー順がそのまま手動の並びとして残ります。順番が急に変わって見えることはありません。確認画面で「キャンセル」を押せば、何も変わらずに閉じます。
有効にしても、Shopify 管理画面の「商品管理」→「コレクション」の一覧には何も変化はありません。コレクションの数も中身もそのままです。

変わるのは、個々のコレクションの詳細画面にある並び順です。有効にしたコレクションを開くと、並び順が「手動」になっていることが分かります。

一方、まだ有効にしていないコレクションの並び順は、Shopify の初期値である「関連性が高い順」などのままです。

Shopify のコレクションで選べる並び順は 9 種類あります。どれを選んでいたコレクションでも有効にできますが、在庫を見て並びを変えてくれるものは 1 つもありません。

有効にしたあとで、Shopify の管理画面から商品を手でドラッグして並べ替えても構いません。次にアプリが並べ直すときは、その新しい順番を土台にして売り切れだけを後ろへ送ります。
有効なコレクションの行に出るボタンです。押すと、その場でそのコレクションを並べ直します。
ふだんは在庫の変化を受けてから数分で自動的に並び替わるので、押す必要はありません。使いどころは、有効にした直後に結果を確かめたいとき、設定の判定を変えた直後に反映を早めたいとき、「最終処理」に「反映待ち」が付いたまましばらく変わらないとき、などです。
何度押しても、在庫のある商品の相対的な順番は変わりません。同じ状態で繰り返し押せば、毎回同じ並びになります。
有効なコレクションで「無効にする」を押すと、確認画面が開きます。アプリを削除した後はこの操作ができなくなるため、削除する前に実行するよう書かれています。

「無効にして元に戻す」を押すと、末尾へ移していた商品を記録しておいた位置へ戻し、並び順の種類も有効にする前のものへ戻します。うまく戻せたときは「元の並び順に戻しました」と表示されます。
並び順の種類によっては、アプリから書き戻せないことがあります。その場合は「商品の並びを元に戻しました。並び順の設定はコレクション側で選び直してください」と表示されます。商品の並びは戻っているので、Shopify の管理画面でコレクションを開き、並び順のメニューから選び直してください。
アプリをアンインストールする予定があるなら、必ずその前に、有効なコレクションをすべて無効にしておきます。アンインストールした時点でアプリはストアにアクセスできなくなり、並び順を書き戻せなくなるためです。何もせずに削除すると、コレクションは手動の並び順のまま残ります(売り切れ商品は後ろに並んだまま固定されます)。

左のメニューの「設定」を開くと、上から「在庫切れの判定」「判定を変えたときの案内」「言語設定」「おすすめアプリ」の順にカードが並びます。

在庫切れの判定は、ストア全体で 1 つの設定です。コレクションごとに判定を変えることはできません。変えたら、カードの下の「保存する」を押すまで反映されません。
保存すると、有効にしているすべてのコレクションが並べ直しの対象になります。カードの下の案内にあるとおり、反映には数分かかることがあります。
どんな状態になった商品を末尾へ送るかを、3 つの選択肢から選びます。

2 つ目の選択肢は、アパレルのように「サイズが揃っていない商品は見せたくない」ストアに向いています。ただし、売れ筋の商品ほどどこかのサイズが欠けやすいので、人気商品がまとめて後ろへ下がることがあります。売れ筋を上に残したいなら、初期値のほうが向いています。
「在庫数が指定した数以下のとき」を選んだときだけ表示される欄です。ここに入れた数以下になった商品を末尾へ送ります。0 を入れれば、売り切れの商品だけが対象になります。0 以上の整数で入力します。

比べる対象は、その商品のすべてのバリエーションとすべてのロケーションの在庫を足した数です。たとえば S・M・L の 3 サイズがそれぞれ 1 点ずつ残っている商品は、在庫数 3 として扱われます。在庫数に 2 を入れていれば、この商品はまだ後ろへ送られません。
ここに書いたタグが付いている商品は、売り切れても移動しません。複数のタグはカンマで区切って入れます(例: preorder, display-only)。大文字と小文字は区別しないので、Preorder と書いても preorder と書いても同じです。
タグは Shopify 管理画面の商品の画面で、右側の「タグ」欄から付けます。

使いどころは、展示用で販売していない商品、売り切れを見せることに意味がある商品(完売御礼の限定品など)、入荷待ちの予約を別の仕組みで受けている商品などです。
次の商品は、末尾へ送る条件を何にしていても、後ろへ送りません。
どちらの設定も、Shopify 管理画面の商品の画面にある「在庫」の欄で確かめられます。下の画像の商品は、在庫が追跡されていて、在庫切れでも販売するがオフで、販売可能な数が 0 なので、売り切れとして末尾へ送られます。

売り切れかどうかは、在庫の数そのものではなく、Shopify が「その商品をいま購入できるか」として持っている状態で見ています。在庫数だけで判定すると、在庫を追跡していない商品(在庫数は 0 のままでも常に買える)が売り切れとして扱われてしまうためです。
「言語を選択」で管理画面の表示言語を選び、「変更する」を押すと切り替わります。日本語・英語・ドイツ語・フランス語・中国語(簡体・繁体)・韓国語など、20 言語から選べます。

このアプリはストアフロントに文字を出さないので、ここで変わるのは管理画面の表示だけです。海外のスタッフと一緒にストアを運営している場合は、それぞれが読みやすい言語にしておけます。
左のメニューの「アプリ一覧」は、UnReact が公開しているほかの Shopify アプリを、カテゴリーやキーワードで探せる画面です。在庫切れ並び替えの機能そのものとは関係ありません。

このアプリはテーマにブロックも埋め込みアプリも追加しません。テーマエディタで設定する項目はなく、「テーマに追加」の手順もありません。インストールして有効にすれば、それだけでストアのコレクションページの順番が変わります。
お客様がコレクションページを開いて並び替えを何も選んでいなければ、アプリが作った順番で表示されます。下の画像では、Sold out の付いた 2 本が最後の段に並んでいます。

Sold out のラベルや、売り切れ商品の画像の見せ方は、テーマ側の表示です。このアプリはラベルを付けたり外したりしません。
お客様がコレクションページの「Sort」から価格の安い順などを選ぶと、その順番が優先されます。下の画像は同じコレクションを価格の安い順で開いたもので、Sold out の商品が 2 番目に出ています。

これは Shopify の仕様で、ストアフロントで選ばれた並び順は、コレクションに保存された手動の並びより優先されます。テーマによっては「Sort」のメニュー自体を非表示にできるので、おすすめ順で見せたいストアはテーマ側の設定も確かめてください。
ストア内検索の結果は、コレクションではないため並び替えの対象になりません。売り切れの商品も、検索の関連度に応じた位置に出ます。

末尾へ送った商品も、非表示にはなりません。商品ページはそのまま残り、カートボタンはテーマの表示に従って Sold out になります。商品ページの URL から来たお客様や、検索エンジンからの流入はそのまま受けられます。

アプリを使わずに同じことをしたい場合、作り方は大きく 2 つあります。テーマの Liquid で表示の順番だけを変える方法と、Admin API でコレクションの並び順そのものを書き換える方法です。
もっとも手軽なのは、コレクションページのテンプレートで商品のループを 2 回に分ける方法です。1 回目で購入できる商品を、2 回目で売り切れの商品を描きます。
{% paginate collection.products by 24 %}
<ul class="product-grid">
{% for product in collection.products %}
{% if product.available %}
<li>{% render 'card-product', card_product: product %}</li>
{% endif %}
{% endfor %}
{% for product in collection.products %}
{% unless product.available %}
<li>{% render 'card-product', card_product: product %}</li>
{% endunless %}
{% endfor %}
</ul>
{{ paginate | default_pagination }}
{% endpaginate %}
product.available は、その商品のバリエーションのどれかが購入できるときに true になります。「在庫切れでも販売する」がオンの商品や在庫を追跡していない商品も true になるので、判定の考え方はアプリの初期値とほぼ同じです。
ただし、この方法には大きな限界があります。並べ替えているのは「いま表示しているページの 24 件の中」だけです。1 ページ目に売り切れが 3 件あれば 1 ページ目の最後に寄りますが、2 ページ目の在庫のある商品が 1 ページ目に上がってくることはありません。商品数が 1 ページに収まるコレクションでしか、期待どおりには動きません。
テーマを入れ替えるたびに書き直しが要ること、Horizon のようにセクションの作りが大きく違うテーマでは差し込む場所から探し直しになることも、運用上の負担になります。
ページをまたいで並べ替えるには、コレクションの並び順そのものを書き換えるしかありません。手順は、並び順を手動にする、いまの順番を読む、売り切れを後ろへ動かす、の 3 段階です。
まず、コレクションの並び順の種類を手動にします。
mutation SetManual($id: ID!) {
collectionUpdate(input: { id: $id, sortOrder: MANUAL }) {
collection { id sortOrder }
userErrors { field message }
}
}
次に、いまの順番と、商品ごとの購入可否を読みます。sortKey に COLLECTION_DEFAULT を指定すると、コレクションに保存されている順番で返ってきます。
query CollectionProducts($id: ID!, $after: String) {
collection(id: $id) {
products(first: 250, after: $after, sortKey: COLLECTION_DEFAULT) {
nodes { id availableForSale }
pageInfo { hasNextPage endCursor }
}
}
}
最後に、売り切れの商品を後ろへ動かします。collectionReorderProducts は非同期で処理され、戻り値の job が完了するまで順番は確定しません。
// 読み取った順番から、売り切れの商品を後ろへ回した移動の一覧を作る
const buildMoves = (products) => {
const available = products.filter((p) => p.availableForSale);
const soldOut = products.filter((p) => !p.availableForSale);
return [...available, ...soldOut]
.map((p, index) => ({ id: p.id, newPosition: String(index) }))
.filter((move, index) => products[index].id !== move.id);
};
const REORDER = `
mutation Reorder($id: ID!, $moves: [MoveInput!]!) {
collectionReorderProducts(id: $id, moves: $moves) {
job { id done }
userErrors { field message }
}
}`;
// 1 回に渡せる移動は 250 件まで。同じコレクションへの呼び出しは必ず直列にする
for (let i = 0; i < moves.length; i += 250) {
const res = await admin.graphql(REORDER, {
variables: { id: collectionId, moves: moves.slice(i, i + 250) },
});
const { data } = await res.json();
if (data.collectionReorderProducts.userErrors.length) throw new Error('並べ替えに失敗しました');
await waitForJob(data.collectionReorderProducts.job.id); // job.done が true になるまで待つ
}
buildMoves は、在庫のある商品をいまの順番のまま前に、売り切れの商品をいまの順番のまま後ろに並べ直し、位置が変わる商品だけを移動として返します。並列に呼ぶと結果が不定になると公式ドキュメントに明記されているので、同じコレクションへの呼び出しは 1 つずつ終わらせてから次へ進めます。
上のコードは「1 回並べ替える」ところまでです。売り切れのたびに自動で並べ直すには、ここから先がむしろ本番になります。
どれも動かなくてもエラーが出にくい部分で、「なぜか並び替わらない」「なぜか 10 分おきに並べ替えている」といった形で表に出ます。月額 $1.99 でこの部分をまとめて任せられるのが、アプリを使う理由になります。
売り切れ商品の扱いを変えるアプリは、大きく「後ろへ送る」ものと「一覧から隠す」ものに分かれます。海外には、Sink | Push Down Out of Stock、PushLast Out of Stock Sold Out、Hide Out Of Stock & Push Down、Pushy | Push Down Out Of Stock といった押し下げのアプリがあり、UnReact にも売り切れ商品の表示を変える「シンプル売り切れ非表示|在庫切れ商品の表示変更」があります。ここでは、それらと組み合わせるときの考え方と手順をまとめます。
並び替えのアプリはどれも、コレクションの順番を書き換えます。同じコレクションで 2 つのアプリを有効にすると、片方が並べた順番をもう片方が書き換え、それにまた片方が反応する、ということが起こりえます。押し下げのアプリを併用するなら、コレクションごとにどちらか片方だけを有効にしてください。
コレクション一覧の「末尾へ移動」と「最終処理」が、在庫が動いていないのに頻繁に変わるときは、別のアプリが同じコレクションを並べ替えていないか確かめてください。
すでに別の押し下げアプリを使っている場合は、次の順で乗り換えると、元の並びを失わずに済みます。
このアプリは、有効にした瞬間に表示されている順番を土台にします。1 を飛ばして有効にすると、前のアプリが作った順番(売り切れが後ろにある状態)を土台にすることになり、無効にしたときにも前のアプリの順番へ戻ります。
商品ページを残したまま一覧の後ろへ送るのが押し下げ、一覧から消してしまうのが非表示です。再入荷を待っているお客様がいる商品、検索エンジンから人が来ている商品、売り切れていることを見せたい限定品は、押し下げのほうが向いています。二度と入荷しない商品や、一覧に残すと問い合わせが増える商品は、非表示や商品のアーカイブが向いています。
同じコレクションで両方を使う場合は、非表示のアプリが一覧から外した商品の位置を、このアプリが並べ替えることになります。表示の結果は非表示のアプリの方式によって変わるので、片方ずつ有効にして、コレクションページで見え方を確かめながら進めてください。
シンプル在庫切れ並び替えは、売り切れた商品をコレクションの最後尾へ自動で移し、在庫が戻れば元の位置へ戻すアプリです。
お客様が自分で並び替えを選んだとき、検索結果、/collections/all の並びは変えられません。そこを踏まえたうえで、コレクションページの先頭をいつも買える商品で埋めておきたいストアに向いています。料金は月額 $1.99 で、7 日間は無料です。
Shopify App Storeから、まずは 1 つのコレクションで有効にして、並びの変化を確かめてみてください。


UnReact が開発している、在庫や商品ページに関わるアプリも紹介します。