wp-shopify
v2.1.1
Published
Vue mixin + store module for WP-Shopify. Integrates Vue, WordPress, and Shopify.
Downloads
32
Readme
Vue plugin to ease Shopify products and cart management. Can be combined with WP-Shopify for Vue + WordPress.
Table of Contents
Installation
If you're combining this plugin with its optional WordPress backend, follow the instructions on that repo first.
Set up a Shopify store and create a new private app.
Go to your Shopify admin panel (your-store.myshopify.com/admin).
Click "Manage Private Apps" at the bottom of the screen. You'll end up on your-store.myshopify.com/admin/apps/private.
Click "Create a new private app."
Keep the default API permissions and make sure
Allow this app to access your storefront data using the Storefront API
at the bottom of the screen is checked.Hit Save to continue.
Note the Storefront Token on the bottom of the next page:
npm install wp-shopify
Before creating your Vue instance, install the plugin with:
import WpShopify from 'wp-shopify' import store from 'your-main-vuex-store' Vue.use(WpShopify, { // your Shopify domain, formatted: my-site.myshopify.com // (Vuepress default shown) domain: jsonData.site.shopifyDomain, // your Shopify storefront access token // (Vuepress default shown) token: jsonData.site.storefrontToken, store: store, // your Vuex store wordpress: true // whether or not to include WordPress data - default false })
That's it! You've got full access to the store module, mixin, and instance variables below.
The Mixin
The plugin automatically adds the following mixin data on all components:
{
props: {
productId: {
type: String,
default: ''
}
},
data() {
return {
selectedVariantIndex: 0,
product: null
}
},
async mounted(){
/*
The mounted function checks to see if the `productId` prop is set.
If it is, mounted() sets `this.product` to the result of `this.getProduct(this.productId)`.
*/
},
methods: {
async getProduct(id){
/*
`getProduct` fetches and returns the product with the given ID from Shopify.
First, it checks the cached data in `$store.state.shopify.productData[id]`;
if nothing is found, it builds and executes a query for the product data from Shopify, caching the result.
*/
},
async getVariant(variant, product){
/*
TODO
*/
},
addToCart(product){
/*
TODO
*/
}
},
computed: {
selectedVariant(){
/*
Returns either the currently selected variant or,
if none is selected or no product is present, null.
*/
}
}
}
Product Data
Data fetched with getProduct
is formatted like this:
{
// Shopify ID (base64-encoded string from Shopify)
id: 'Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0Lzk1NTIzMjI1NjAzNg==',
// Shopify title
title: 'My Product',
// Shopify description as raw HTML
descriptionHtml: '',
// List of variants
variants: [
{
// Price for this variant
price: '30.00',
// Compare to sale price for this variant
compareAtPrice: '30.00',
// Variant title
title: 'Small',
// Whether or not this variant is available
availableForSale: true,
// Variant ID
id: 'Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0VmFyaWFudC85NDE3NTIzMzMxMTA4'
}
],
// If `wordpress` was set to `true` when installing plugin:
wp: {
// relative path to this product in WordPress
path: '/store/sample-product/'
}
}
Store Module
WP-Shopify adds a module called shopify
to the Vuex store with the following state, mutations, and actions:
state: {
// Cache for fetched product data
productData: {},
// load initial cart state from localStorage (defaults to empty array)
cart: loadCart(),
// domain and token (set in installation)
domain: '',
token: '',
// since we can't deep-watch the cart for quantities, etc, the store
// watches this value and increment every time the cart is modified
// (this is for internal use only)
cartVersion: 0,
// checkout URL as calculated from cart contents
checkoutUrl: '',
// subtotal of all items in cart
subtotal: ''
},
mutations: {
UPDATE_CACHED_RESULT({ shopifyId, data }) {
/*
Updates `state.productData` entry for `shopifyId` with new `data`
*/
},
ADD_TO_CART({ variant, quantity }) {
/*
Adds `quantity` (default 1) of the given `variant` to the cart.
Updates localstorage and the checkout URL.
*/
},
SET_QUANTITY({ variant, quantity, changeBy }) {
/*
Either set the `variant` to a specific `quantity` or change its current
`quantity` by `changeBy` units. Updates localstorage and the checkout URL.
*/
},
REMOVE_FROM_CART({ variant }) {
/*
Removes all of a given `variant` from the cart.
Updates localstorage and the checkout URL.
*/
},
EMPTY_CART() {
/*
Removes everything from the cart.
Updates localstorage and the checkout URL.
*/
},
UPDATE_CHECKOUT() {
/*
Manually refresh the checkout URL. Usually called internally.
*/
},
SET_DOMAIN_AND_TOKEN({ domain, token, force }) {
/*
Update the Shopify domain and token.
Set `force` to `true` to update even if a Shopify domain and token already exist.
*/
}
},
actions: {
async GET_PRODUCT_DATA(
{ shopifyId, domain, token }
) {
/*
Fetch and cache a product's data given its ID.
Should not be called manually - use `getProduct` from the mixin instead.
*/
}
}
Product Templating
Basic Templating
All of a product's Shopify data is contained in the product object. If you know the ID of your product, you can retrieve the full object with getProduct
.
<template>
<section>
{{ product ? product.title : '' }}
</section>
</template>
<script>
export default {
mounted() {
// this is how the WordPress plugin retrieves a product...
const productId = this.$store.getters.post.productId
// ...but any valid ID will work
this.product = this.getProduct(productId)
}
}
</script>
You can also pass the ID as a prop called product-id
to automatically fetch a product on a custom component:
Main.vue
<template>
<product-example :product-id="'your product ID here'"/>
</template>
ProductExample.vue
<template>
<div class="product-example">
{{ product ? product.title : '' }}
</div>
</template>
Variants
A product usually has at least one variant - different colors, sizes, etc. - that you'll need to account for.
The easiest way to handle this is to use the built-in selectedVariant
system. For example:
<template>
<h2 v-if="!product">Loading...</h2>
<main v-else>
<h2>{{ product.title }}</h2>
<h3>Currently selected: {{ selectedVariant.title }} (${{ selectedVariant.price }})</h3>
<select v-model="selectedVariantIndex">
<option
v-for="(variant, i) in variants"
:key="i"
:value="i">
{{ variant.title }}
</option>
</select>
<button @click="addToCart()">
Add to Cart
</button>
</main>
</template>
And an annotated version of the above:
```html
<template>
<!-- Product loading state -->
<h2 v-if="!product">Loading...</h2>
<!-- Product loaded and ready -->
<main v-else>
<h2>{{ product.title }}</h2>
<h3>Currently selected: {{ selectedVariant.title }} (${{ selectedVariant.price }})</h3>
<!-- Changing the selectedVariantIndex changes the selectedVariant -->
<select v-model="selectedVariantIndex">
<option
v-for="(variant, i) in variants"
:key="i"
:value="i">
{{ variant.title }}
</option>
</select>
<!-- addToCart is another function from the mixin -->
<button @click="addToCart()">
Add to Cart
</button>
</main>
</template>
Cart
A shopping cart might look like this:
<template>
<div class="shopping-cart">
<ul>
<li v-for="(product, i) in $store.state.shopify.cart" :key="i">
<span>{{ product.title }}</span>
<span>({{ product.quantity }})</span>
</li>
</ul>
<a :href="checkoutUrl" target="_blank" rel="noopener noreferrer">Checkout</a>
</div>
</template>
Annotated:
<template>
<div class="shopping-cart">
<ul>
<!-- Shopify data is kept in a Vuex module called 'shopify' -->
<li v-for="(product, i) in $store.state.shopify.cart" :key="i">
<span>{{ product.title }}</span>
<span>({{ product.quantity }})</span>
</li>
</ul>
<!-- The checkout URL is automatically updated any time the cart changes -->
<a :href="checkoutUrl" target="_blank" rel="noopener noreferrer">Checkout</a>
</div>
</template>