Im building a website with Nuxt and headless wordpress. All data is coming from wordpress api (wp-json).
I'm having an issue with navigation menu, with parent/child menu items.
On parent element click, every menu item that has children is triggered at once.
I need it to trigger only the one I click on.
Here's what happens #click right now
Heres what I have so far:
HTML:
<nav>
<div class="">
<!-- Triggers burger menu -->
<ul :class="isOpen ? 'block' : 'hidden'" class="sm:flex">
<!-- Loop through the parent navigation -->
<!-- Add click event to trigger dropdown menu -->
<li class="sm:mx-8 text-white cursor-pointer py-4 sm:py-0"
v-for="item in linksArray"
:key="item.ID"
#click="isOpen = !isOpen"
>
<!-- Insert the data from wp-json -->
<div class="">
<!-- Parent navigation elements -->
<div class="flex items-center">
<div class=""> {{item.title}} </div>
</div>
<!-- Child navigation here -->
<div>
<!-- Trigger show or hide child elements on click from above -->
<ul
v-show="isOpen"
class="sm:absolute bg-gray-800 rounded mt-4"
>
<!-- Loop through the child elements -->
<li class="text-white rounded hover:bg-gray-700 cursor-pointer py-2 px-2"
v-for="childItem in item.child_items"
:key="childItem.ID"
>
<!-- Insert the data for children from wp-json -->
<nuxt-link :to="childItem.url">
<div class="flex items-center">
<span class="title">
{{ childItem.title }}
</span>
</div>
</nuxt-link>
</li>
</ul>
</div>
</div>
</li>
</ul>
</div>
</nav>
Script:
<script>
export default {
name: "Navbar",
props: {
linksArray: {
type: Array
},
},
data() {
return {
isOpen: false,
};
},
watch: {
// Use to close the menu on route change
'$route'() {
this.isOpen = false;
},
}
};
</script>
wp-json menu response
{
"term_id": 2,
"name": "Main",
"slug": "main",
"term_group": 0,
"term_taxonomy_id": 2,
"taxonomy": "nav_menu",
"description": "",
"parent": 0,
"count": 14,
"filter": "raw",
"items": [
{
"ID": 319,
"post_author": "1",
"post_date": "2020-01-11 21:42:33",
"post_date_gmt": "2020-01-11 21:42:33",
"post_content": "",
"post_title": "Games",
"post_excerpt": "",
"post_status": "publish",
"comment_status": "closed",
"ping_status": "closed",
"post_password": "",
"post_name": "games",
"to_ping": "",
"pinged": "",
"post_modified": "2020-01-15 21:10:20",
"post_modified_gmt": "2020-01-15 21:10:20",
"post_content_filtered": "",
"post_parent": 0,
"guid": "http://localhost/?p=319",
"menu_order": 1,
"post_type": "nav_menu_item",
"post_mime_type": "",
"comment_count": "0",
"filter": "raw",
"db_id": 319,
"menu_item_parent": "0",
"object_id": "319",
"object": "custom",
"type": "custom",
"type_label": "Custom Link",
"title": "Games",
"url": "#",
"target": "",
"attr_title": "",
"description": "",
"classes": [],
"xfn": "",
"logo": {},
"child_items": [
{
"ID": 321,
"post_author": "1",
"post_date": "2020-01-11 21:42:33",
"post_date_gmt": "2020-01-11 21:42:33",
"post_content": "",
"post_title": "Dota 2",
"post_excerpt": "",
"post_status": "publish",
"comment_status": "closed",
"ping_status": "closed",
"post_password": "",
"post_name": "dota-2",
"to_ping": "",
"pinged": "",
"post_modified": "2020-01-15 21:10:20",
"post_modified_gmt": "2020-01-15 21:10:20",
"post_content_filtered": "",
"post_parent": 0,
"guid": "http://localhost/?p=321",
"menu_order": 2,
"post_type": "nav_menu_item",
"post_mime_type": "",
"comment_count": "0",
"filter": "raw",
"db_id": 321,
"menu_item_parent": "319",
"object_id": "301",
"object": "games",
"type": "post_type",
"type_label": "Game",
"url": "http://localhost/games/dota-2/",
"title": "Dota 2",
"target": "",
"attr_title": "",
"description": "",
"classes": [
""
],
"xfn": "",
"logo": {},
"slug": "dota-2"
},
{
"ID": 322,
"post_author": "1",
"post_date": "2020-01-11 21:42:33",
"post_date_gmt": "2020-01-11 21:42:33",
"post_content": "",
"post_title": "Overwatch",
"post_excerpt": "",
"post_status": "publish",
"comment_status": "closed",
"ping_status": "closed",
"post_password": "",
"post_name": "overwatch",
"to_ping": "",
"pinged": "",
"post_modified": "2020-01-15 21:10:20",
"post_modified_gmt": "2020-01-15 21:10:20",
"post_content_filtered": "",
"post_parent": 0,
"guid": "http://localhost/?p=322",
"menu_order": 3,
"post_type": "nav_menu_item",
"post_mime_type": "",
"comment_count": "0",
"filter": "raw",
"db_id": 322,
"menu_item_parent": "319",
"object_id": "264",
"object": "games",
"type": "post_type",
"type_label": "Game",
"url": "http://localhost/games/overwatch/",
"title": "Overwatch",
"target": "",
"attr_title": "",
"description": "",
"classes": [
""
],
"xfn": "",
"logo": {},
"slug": "overwatch"
},
{},
{},
{}
]
},
{
"ID": 320,
"post_author": "1",
"post_date": "2020-01-11 21:42:33",
"post_date_gmt": "2020-01-11 21:42:33",
"post_content": "",
"post_title": "Gambling",
"post_excerpt": "",
"post_status": "publish",
"comment_status": "closed",
"ping_status": "closed",
"post_password": "",
"post_name": "gambling",
"to_ping": "",
"pinged": "",
"post_modified": "2020-01-15 21:10:20",
"post_modified_gmt": "2020-01-15 21:10:20",
"post_content_filtered": "",
"post_parent": 0,
"guid": "http://localhost/?p=320",
"menu_order": 7,
"post_type": "nav_menu_item",
"post_mime_type": "",
"comment_count": "0",
"filter": "raw",
"db_id": 320,
"menu_item_parent": "0",
"object_id": "320",
"object": "custom",
"type": "custom",
"type_label": "Custom Link",
"title": "Gambling",
"url": "#",
"target": "",
"attr_title": "",
"description": "",
"classes": [
""
],
"xfn": "",
"logo": {},
"child_items": [
{
"ID": 336,
"post_author": "1",
"post_date": "2020-01-12 00:46:53",
"post_date_gmt": "2020-01-12 00:46:53",
"post_content": "",
"post_title": "CSGO Gambling",
"post_excerpt": "",
"post_status": "publish",
"comment_status": "closed",
"ping_status": "closed",
"post_password": "",
"post_name": "csgo-gambling",
"to_ping": "",
"pinged": "",
"post_modified": "2020-01-15 21:10:20",
"post_modified_gmt": "2020-01-15 21:10:20",
"post_content_filtered": "",
"post_parent": 0,
"guid": "http://localhost/?p=336",
"menu_order": 8,
"post_type": "nav_menu_item",
"post_mime_type": "",
"comment_count": "0",
"filter": "raw",
"db_id": 336,
"menu_item_parent": "320",
"object_id": "336",
"object": "custom",
"type": "custom",
"type_label": "Custom Link",
"title": "CSGO Gambling",
"url": "/csgo/gambling",
"target": "",
"attr_title": "",
"description": "",
"classes": [
""
],
"xfn": "",
"logo": {}
}
]
},
{},
{},
{}
]
}
I'm new to development, and been stuck on this for over a week now.
Don't know where to look for answers. Found nothing on here or google.
Could you please point me at what I'm doing wrong or at the solution please.
Thanks!
In your code, all children are using the same variable to say if they should be shown or not. As a result, if this variable is true then all children will be show, and if it's false then all will be hidden.
You need track which menu items you want to show. Here is an example of how you could achieve this:
new Vue({
el: "#app",
data: {
menu: [{
title: "Main one",
children: ['child 1', 'child 2', 'child 3']
},
{
title: "Main two",
children: ['child 4', 'child 5', 'child 6']
},
{
title: "Main three",
children: ['child 7']
},
{
title: "Main four",
children: ['child 8', 'child 9']
}
],
openMenu: null
},
methods: {
showChildren(i) {
if (this.openMenu === i) this.openMenu = null
else this.openMenu = i
}
}
})
li {
margin: 8px;
padding: 8px;
background: pink;
cursor: pointer;
}
<div id="app">
<h2>menu:</h2>
<ul>
<li v-for="(nav, i) in menu" #click="showChildren(i)">
{{nav.title}}
<ul v-show="openMenu === i" v-for="child in nav.children">
<li>{{ child }}</li>
</ul>
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
Basically, this works by only showing the children if openMenu is the same as the parent items index within the array.
When you click the parent, it sends its index to a function function which checks if it's already open (openMenu is equal to the parents index), and if so changes openMenu to null to hide the children. Else it sets openMenu to the parent index, making v-show="openMenu === i" valid for that parent.
Related
i am searching from a whole day long for a easy way to create a dynamic wordpress nested navbar.
now i am looking for a function if there is to get all pages list as a nested object
i am using get_pages() which is giving me flat array with parent_page_id
[
{
"ID": 2,
"post_author": "1",
"post_date": "2021-10-10 05:21:26",
"post_date_gmt": "2021-10-10 05:21:26",
"post_content": "<!-- wp:paragraph -->\n<p>This is an example page. It's different from a blog post because it will stay in one place and will show up in your site navigation (in most themes). Most people start with an About page that introduces them to potential site visitors. It might say something like this:</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:quote -->\n<blockquote class=\"wp-block-quote\"><p>Hi there! I'm a bike messenger by day, aspiring actor by night, and this is my website. I live in Los Angeles, have a great dog named Jack, and I like piña coladas. (And gettin' caught in the rain.)</p></blockquote>\n<!-- /wp:quote -->\n\n<!-- wp:paragraph -->\n<p>...or something like this:</p>\n<!-- /wp:paragraph -->\n\n<!-- wp:quote -->\n<blockquote class=\"wp-block-quote\"><p>The XYZ Doohickey Company was founded in 1971, and has been providing quality doohickeys to the public ever since. Located in Gotham City, XYZ employs over 2,000 people and does all kinds of awesome things for the Gotham community.</p></blockquote>\n<!-- /wp:quote -->\n\n<!-- wp:paragraph -->\n<p>As a new WordPress user, you should go to your dashboard to delete this page and create new pages for your content. Have fun!</p>\n<!-- /wp:paragraph -->",
"post_title": "Sample Page",
"post_excerpt": "",
"post_status": "publish",
"comment_status": "closed",
"ping_status": "closed",
"post_password": "",
"post_name": "sample-page",
"to_ping": "",
"pinged": "",
"post_modified": "2021-10-10 19:14:00",
"post_modified_gmt": "2021-10-10 19:14:00",
"post_content_filtered": "",
"post_parent": 0,
"guid": "http://localhost:8000/?page_id=2",
"menu_order": 2,
"post_type": "page",
"post_mime_type": "",
"comment_count": "0",
"filter": "raw"
},
{
"ID": 28,
"post_author": "1",
"post_date": "2021-10-10 19:11:08",
"post_date_gmt": "2021-10-10 19:11:08",
"post_content": "<!-- wp:paragraph -->\n<p></p>\n<!-- /wp:paragraph -->\n\n<!-- wp:columns -->\n<div class=\"wp-block-columns\"><!-- wp:column {\"width\":\"100%\"} -->\n<div class=\"wp-block-column\" style=\"flex-basis:100%\"><!-- wp:query {\"queryId\":21,\"query\":{\"perPage\":\"100\",\"pages\":0,\"offset\":0,\"postType\":\"post\",\"categoryIds\":[4,3,1],\"tagIds\":[],\"order\":\"desc\",\"orderBy\":\"date\",\"search\":\"\",\"exclude\":[],\"sticky\":\"exclude\",\"inherit\":false},\"displayLayout\":{\"type\":\"flex\",\"columns\":4}} -->\n<div class=\"wp-block-query\"><!-- wp:post-template -->\n<!-- wp:post-featured-image /-->\n\n<!-- wp:post-title {\"isLink\":true,\"textColor\":\"vivid-cyan-blue\",\"fontSize\":\"medium\"} /-->\n\n<!-- wp:post-date {\"fontSize\":\"small\"} /-->\n<!-- /wp:post-template --></div>\n<!-- /wp:query --></div>\n<!-- /wp:column --></div>\n<!-- /wp:columns -->",
"post_title": "Products",
"post_excerpt": "",
"post_status": "publish",
"comment_status": "closed",
"ping_status": "closed",
"post_password": "",
"post_name": "products",
"to_ping": "",
"pinged": "",
"post_modified": "2021-10-11 22:01:39",
"post_modified_gmt": "2021-10-11 22:01:39",
"post_content_filtered": "",
"post_parent": 0,
"guid": "http://localhost:8000/?page_id=28",
"menu_order": 1,
"post_type": "page",
"post_mime_type": "",
"comment_count": "0",
"filter": "raw"
},
{
"ID": 156,
"post_author": "1",
"post_date": "2021-10-11 23:25:00",
"post_date_gmt": "2021-10-11 23:25:00",
"post_content": "",
"post_title": "dresses",
"post_excerpt": "",
"post_status": "publish",
"comment_status": "closed",
"ping_status": "closed",
"post_password": "",
"post_name": "dresses",
"to_ping": "",
"pinged": "",
"post_modified": "2021-10-11 23:25:00",
"post_modified_gmt": "2021-10-11 23:25:00",
"post_content_filtered": "",
"post_parent": 28,
"guid": "http://localhost:8000/?page_id=156",
"menu_order": 0,
"post_type": "page",
"post_mime_type": "",
"comment_count": "0",
"filter": "raw"
},
{
"ID": 158,
"post_author": "1",
"post_date": "2021-10-11 23:45:04",
"post_date_gmt": "2021-10-11 23:45:04",
"post_content": "",
"post_title": "2 level child",
"post_excerpt": "",
"post_status": "publish",
"comment_status": "closed",
"ping_status": "closed",
"post_password": "",
"post_name": "2-level-child",
"to_ping": "",
"pinged": "",
"post_modified": "2021-10-12 00:03:19",
"post_modified_gmt": "2021-10-12 00:03:19",
"post_content_filtered": "",
"post_parent": 156,
"guid": "http://localhost:8000/?page_id=158",
"menu_order": 0,
"post_type": "page",
"post_mime_type": "",
"comment_count": "0",
"filter": "raw"
},
{
"ID": 5,
"post_author": "1",
"post_date": "2021-10-10 06:48:19",
"post_date_gmt": "2021-10-10 06:48:19",
"post_content": "[contact-form-7 id=\"16\" title=\"Contact Us\"]",
"post_title": "Contact Us",
"post_excerpt": "",
"post_status": "publish",
"comment_status": "closed",
"ping_status": "closed",
"post_password": "",
"post_name": "contact-us",
"to_ping": "",
"pinged": "",
"post_modified": "2021-10-10 19:14:09",
"post_modified_gmt": "2021-10-10 19:14:09",
"post_content_filtered": "",
"post_parent": 0,
"guid": "http://localhost:8000/?page_id=5",
"menu_order": 3,
"post_type": "page",
"post_mime_type": "",
"comment_count": "0",
"filter": "raw"
}
]
now what i want is a nested object so i can make recursive function to iterate and generate fully custom navbar
is there any way to achieve such nested object ? its a easy question for an experienced wp developer i am new so i dont know much about available functions though i have researched quit well
You could use the built-in interface and function
when using menus... first register a location/several location
/*** REGISTER MENU ***/
register_nav_menus(array(
'topmenu' => __( 'Top Menu', THEME_NAME ),
'topmenumobile' => __( 'Top Mobile Menu', THEME_NAME ),
'footermenu' => __( 'Footer Menu', THEME_NAME ),
'footermenumob' => __( 'Footer Mobile Menu', THEME_NAME ),
));
Then build your menu in the backend:
Now you can output it the frontend using wp_nav_menu
for example:
<?php
wp_nav_menu( array(
'theme_location' => (wp_is_mobile() ? 'topmenumobile' : 'topmenu'),
'depth' => 2,
'container' => 'div',
'container_class' => 'collapse navbar-collapse',
'container_id' => 'topmenu',
'menu_class' => 'nav navbar-nav',
) );
?>
On the resulting HTML, you can apply CSS and JS or even use a walker library if your working with bootstrap (for example).
Hope this helps, I am not sure I completely understand your needs so I here for edits and additional information.
I'm trying to add latest posts to my footer menu, so I used wp_get_nav_menu_items filter.
I'm going to skip the filter itself, just in the end of the function I get an array(2) of posts. I've added all the additional data to them (like menu_parent_id etc.). The problem is final menu contains only the last post. Even if I set $items[0] = $items[1]; so that both items are identical. It does not depend on a particular menu or post amount - always the last item only. I'll leave the code in quickly-reproducible format: just a filter and JSON-encoded items:
add_filter('wp_get_nav_menu_items', function ($items, $menu, $args) {
return json_decode('[{
"ID": 2576,
"post_author": "3",
"post_date": "2020-05-11 23:46:15",
"post_date_gmt": "2020-05-11 23:46:15",
"post_content": "",
"post_title": "Testr",
"post_excerpt": "",
"post_status": "publish",
"comment_status": "open",
"ping_status": "open",
"post_password": "",
"post_name": "testr",
"to_ping": "",
"pinged": "",
"post_modified": "2020-05-11 23:46:15",
"post_modified_gmt": "2020-05-11 23:46:15",
"post_content_filtered": "",
"post_parent": 0,
"guid": "http:\/\/mysite.local\/?p=2576",
"menu_order": 0,
"post_type": "post",
"post_mime_type": "",
"comment_count": "0",
"filter": "raw",
"db_id": 2576,
"object_id": "",
"menu_item_parent": "0",
"url": "http:\/\/mysite.local\/?p=2576",
"title": "Testr",
"target": "",
"classes": [],
"type": "",
"object": "",
"attr_title": "",
"description": "",
"xfn": "",
"status": ""
}, {
"ID": 3439,
"post_author": "3",
"post_date": "2020-05-11 23:28:49",
"post_date_gmt": "2020-05-11 23:28:49",
"post_content": "",
"post_title": "Test",
"post_excerpt": "",
"post_status": "publish",
"comment_status": "open",
"ping_status": "open",
"post_password": "",
"post_name": "test-2",
"to_ping": "",
"pinged": "",
"post_modified": "2020-05-11 23:28:49",
"post_modified_gmt": "2020-05-11 23:28:49",
"post_content_filtered": "",
"post_parent": 0,
"guid": "http:\/\/mysite.local\/?p=3439",
"menu_order": 0,
"post_type": "post",
"post_mime_type": "",
"comment_count": "0",
"filter": "raw",
"db_id": 3439,
"object_id": "",
"menu_item_parent": "0",
"url": "http:\/\/mysite.local\/?p=3439",
"title": "Test",
"target": "",
"classes": [],
"type": "",
"object": "",
"attr_title": "",
"description": "",
"xfn": "",
"status": ""
}]');
});
Did anyone have this issue before, please help, it drives me nuts :)
I figured out what was the problem. Each menu item must contain a menu_order property, or else wordpress sorting function will overwrite the same value over and over again showing only the last post.
I have updated my WordPress from 4.6.x to 5.2.1.
I have also updated plugin ACF Pro from 4.x.x to 5.8.1.
Since then, ACF labels of type 'repeater' doesn't work anymore :
they don't appear anymore in admin panel and in front of application.
The other labels work well.
I don't know why. Do you have an idea ?
{
"key": "field_56f04b4f1d998",
"label": "Layouts",
"name": "layouts",
"type": "repeater",
"instructions": "",
"required": 0,
"conditional_logic": [],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"collapsed": "",
"min": "",
"max": "",
"layout": "block",
"button_label": "Add layout",
"sub_fields": [
{
"key": "field_573c2f5d67d89",
"label": "Type",
"name": "type",
"type": "select",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"layout100": "1\/1",
"layout100_full": "1\/1 full",
"layout5050": "1\/2 + 1\/2",
"layout3366": "1\/3 + 2\/3",
"layout6633": "2\/3 + 1\/3"
},
"default_value": [
0
],
"allow_null": 0,
"multiple": 0,
"ui": 0,
"ajax": 0,
"return_format": "value",
"placeholder": ""
},
{
"key": "field_5a266ce145c75",
"label": "Border top ?",
"name": "border_top",
"type": "true_false",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"message": "",
"default_value": 1
}
]
}
Update acf-component-field plugin and add 'field_group_key=component_group_id' in your json.
I have a field group named 'Sales Item' and 'Invoice'. The invoice field group have a 'Items' field which is a repeater field, the 'Items' field contains the 'Sales Item' field group as clone field.
I need to create an Invoice post type programmatically. All the field of the 'Invoice' is created except the 'Items' field. How do I add the data to the 'Items' reaper field which contain the 'Sales Item' clone field?
Sales Item and Invoice field group.
[
{
"key": "group_5c4033565a799",
"title": "Invoice",
"fields": [
{
"key": "field_5c44150d123de",
"label": "Type",
"name": "ims_invoice_type",
"type": "select",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"choices": {
"sales": "Sales",
"purchases": "Purchases"
},
"default_value": [
"purchases"
],
"allow_null": 0,
"multiple": 0,
"ui": 0,
"return_format": "value",
"ajax": 0,
"placeholder": ""
},
{
"key": "field_5c4033c40bce3",
"label": "Invoice Number",
"name": "ims_invoice_invoice_number",
"type": "text",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "",
"maxlength": "",
"placeholder": "",
"prepend": "",
"append": ""
},
{
"key": "field_5c4e943170485",
"label": "Customer",
"name": "ims_invoice_customer",
"type": "relationship",
"instructions": "",
"required": 1,
"conditional_logic": [
[
{
"field": "field_5c44150d123de",
"operator": "==",
"value": "sales"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"post_type": [
"ims_customer"
],
"taxonomy": "",
"filters": [
"search",
"post_type",
"taxonomy"
],
"elements": "",
"min": 1,
"max": 1,
"return_format": "object"
},
{
"key": "field_5c4033620bce1",
"label": "Vendor",
"name": "ims_invoice_vendor",
"type": "relationship",
"instructions": "",
"required": 1,
"conditional_logic": [
[
{
"field": "field_5c44150d123de",
"operator": "==",
"value": "purchases"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"post_type": [
"ims_vendor"
],
"taxonomy": "",
"filters": [
"search",
"post_type",
"taxonomy"
],
"elements": "",
"min": 1,
"max": 1,
"return_format": "object"
},
{
"key": "field_5c40339b0bce2",
"label": "Date",
"name": "ims_invoice_date",
"type": "date_picker",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"display_format": "F j, Y",
"return_format": "d\/m\/Y",
"first_day": 0
},
{
"key": "field_5c4034030cff9",
"label": "Items",
"name": "ims_invoice_sales_items",
"type": "repeater",
"instructions": "",
"required": 1,
"conditional_logic": [
[
{
"field": "field_5c44150d123de",
"operator": "==",
"value": "sales"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"collapsed": "",
"min": 0,
"max": 0,
"layout": "table",
"button_label": "Add Product",
"sub_fields": [
{
"key": "field_5c443f4dd87de",
"label": "Item",
"name": "ims_invoice_sales_items",
"type": "clone",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"clone": [
"group_5c443e86ad330"
],
"display": "seamless",
"layout": "block",
"prefix_label": 0,
"prefix_name": 1
}
]
},
{
"key": "field_5c78f715c5efe",
"label": "Items",
"name": "ims_invoice_purchases_items",
"type": "repeater",
"instructions": "",
"required": 1,
"conditional_logic": [
[
{
"field": "field_5c44150d123de",
"operator": "==",
"value": "purchases"
}
]
],
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"collapsed": "",
"min": 0,
"max": 0,
"layout": "table",
"button_label": "Add Product",
"sub_fields": [
{
"key": "field_5c78f730c5eff",
"label": "Items",
"name": "ims_invoice_purchases_items",
"type": "clone",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"clone": [
"group_5c78e0e32443c"
],
"display": "seamless",
"layout": "block",
"prefix_label": 0,
"prefix_name": 1
}
]
},
{
"key": "field_5c41b2ff17b09",
"label": "Sub Total",
"name": "ims_invoice_sub_total",
"type": "read_only",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"copy_to_clipboard": 0,
"display_type": "text"
},
{
"key": "field_5c41b31a17b0b",
"label": "Discount",
"name": "ims_invoice_discount",
"type": "number",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "0.0",
"placeholder": "",
"prepend": "",
"append": "",
"min": 0,
"max": "",
"step": ""
},
{
"key": "field_5c41b32617b0c",
"label": "V.A.T",
"name": "ims_invoice_vat",
"type": "number",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "0.0",
"placeholder": "",
"prepend": "",
"append": "",
"min": 0,
"max": "",
"step": ""
},
{
"key": "field_5c41b33317b0d",
"label": "Total",
"name": "ims_invoice_total",
"type": "read_only",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"copy_to_clipboard": 0,
"display_type": "text"
},
{
"key": "field_5c41b33b17b0e",
"label": "Paid",
"name": "ims_invoice_paid",
"type": "number",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "0.0",
"placeholder": "",
"prepend": "",
"append": "",
"min": 0,
"max": "",
"step": ""
},
{
"key": "field_5c41b34517b0f",
"label": "Due",
"name": "ims_invoice_due",
"type": "read_only",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"copy_to_clipboard": 0,
"display_type": "text"
}
],
"location": [
[
{
"param": "post_type",
"operator": "==",
"value": "ims_invoice"
}
]
],
"menu_order": 0,
"position": "normal",
"style": "default",
"label_placement": "left",
"instruction_placement": "label",
"hide_on_screen": "",
"active": 1,
"description": ""
},
{
"key": "group_5c443e86ad330",
"title": "Sales Item",
"fields": [
{
"key": "field_5c443eab22fe1",
"label": "Item",
"name": "sales_item_item",
"type": "post_object",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"post_type": [
"ims_product"
],
"taxonomy": "",
"allow_null": 0,
"multiple": 0,
"return_format": "object",
"ui": 1
},
{
"key": "field_5c779ac395834",
"label": "SKU",
"name": "sales_item_sku",
"type": "read_only",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"copy_to_clipboard": 0,
"display_type": "text"
},
{
"key": "field_5c443eba22fe2",
"label": "Quantity",
"name": "sales_item_quantity",
"type": "number",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": 1,
"placeholder": "",
"prepend": "",
"append": "",
"min": "",
"max": "",
"step": ""
},
{
"key": "field_5c443ec622fe3",
"label": "Unit Price",
"name": "sales_item_unit_price",
"type": "read_only",
"instructions": "",
"required": 1,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"copy_to_clipboard": 0,
"display_type": "text"
},
{
"key": "field_5c443edf22fe4",
"label": "Price",
"name": "sales_item_price",
"type": "read_only",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"copy_to_clipboard": 0,
"display_type": "text"
},
{
"key": "field_5c443efd22fe5",
"label": "Discount",
"name": "sales_item_discount",
"type": "number",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"default_value": "0.0",
"placeholder": "",
"prepend": "",
"append": "",
"min": "",
"max": "",
"step": ""
},
{
"key": "field_5c443f0a22fe6",
"label": "Amount",
"name": "sales_item_amount",
"type": "read_only",
"instructions": "",
"required": 0,
"conditional_logic": 0,
"wrapper": {
"width": "",
"class": "",
"id": ""
},
"copy_to_clipboard": 0,
"display_type": "text"
}
],
"location": [
[
{
"param": "post_type",
"operator": "==",
"value": "post"
}
]
],
"menu_order": 0,
"position": "normal",
"style": "default",
"label_placement": "top",
"instruction_placement": "label",
"hide_on_screen": "",
"active": 0,
"description": ""
}
]
Here is the code, I have written so far.
$invoice_key = 'ims_invoice_sales_items_';
$customer_id = $_POST['customer_id'];
$ims_products = $_POST['ims_product'];
$basket_discount = $_POST['basket_discount'];
$basket_vat = $_POST['basket_vat'];
$paid = $_POST['paid'];
$sub_total = 0.0;
foreach( $ims_products as $ims_product ) {
$id = $ims_product['id'];
$quantity = intval( $ims_product['quantity'] );
$unit_price = floatval( get_field( 'ims_product_mrp', $id ) );
$discount = floatval( $ims_product['discount'] );
$price = $quantity * $unit_price;
$amount = $price - ( $discount * $price ) / 100.0;
$sub_total += $amount;
$items [] = array(
$invoice_key . 'sales_item_item' => $id,
$invoice_key . 'sales_item_sku' => get_field( 'ims_product_sku', $id ),
$invoice_key . 'sales_item_quantity' => $quantity,
$invoice_key . 'sales_item_unit_price' => $unit_price,
$invoice_key . 'sales_item_price' => $price,
$invoice_key . 'sales_item_discount' => $discount,
$invoice_key . 'sales_item_amount' => $amount
);
}
// var_dump( $items );
// Calculate grand total after discount and vat.
$grand_total = $sub_total - ( $basket_discount * $sub_total ) / 100.0;
$grand_total = $grand_total + ( $basket_vat * $grand_total ) / 100.0;
// Calculate due.
$due = $grand_total - $paid;
$meta_inputs = array(
'ims_invoice_type' => 'sales',
'ims_invoice_invoice_number' => uniqid(),
'ims_invoice_customer' => $customer_id,
'ims_invoice_date' => current_time('d/m/Y', false ),
'ims_invoice_sub_total' => $sub_total,
'ims_invoice_discount' => $basket_discount,
'ims_invoice_vat' => $basket_vat,
'ims_invoice_total' => $grand_total,
'ims_invoice_paid' => $paid,
'ims_invoice_due' => $due
);
$invoice_args = array(
'post_type' => 'ims_invoice',
'post_status' => 'publish',
'meta_input' => $meta_inputs
);
// var_dump( $invoice_args );
$post_id = wp_insert_post( $invoice_args, true );
var_dump( "<h1>Post Id = ${post_id}</h1>");
foreach( $items as $item ) {
var_dump( $item );
add_row( 'ims_invoice_sales_items', $item, $post_id );
}
die;
You don't actually need to call add_row and add each repeater entry in sequence. You can push the entire items array in one go. You will need to do following things to make sure that the below conditions are met and the code will work for you.
You have the correct field key for your repeater field (Something to the effect of field_5bfed0e63a470).
The slug of each repeating element in the repeater group is correct(For example repeater group has a field sales item and the slug for it is in the format sales_item_item).
Once You have made sure of that you just need to create an array of the new items set as you have already created with $items variable and call the below code:
$items_inserted = update_field('field_5bfed0e63a470', $items, $post_id);
Things should work fine for you with this. Good Luck!!!
Im playing around with Woo Commerce API (v3.4.4). I can get a product with:
curl -X GET \
'http://localhost/wp-json/wc/v2/products/7?oauth_consumer_key=ck_8cbe42fb09c04954a63994c22145270f27871dec&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1535381325&oauth_nonce=iApWJblA1A5&oauth_version=1.0&oauth_signature=9T1T43FvOpBzVBU+RQS90zMB/XM=' \
-H 'Cache-Control: no-cache' \
-H 'Postman-Token: bdb7f5c9-5719-4bcf-b4fc-38aed3bd5d08'
I get the response:
{
"id": 7,
"name": "Test product",
"slug": "test-product",
"permalink": "http://localhost/product/test-product/",
"date_created": "2018-08-22T15:25:26",
"date_created_gmt": "2018-08-22T15:25:26",
"date_modified": "2018-08-22T15:25:26",
"date_modified_gmt": "2018-08-22T15:25:26",
"type": "simple",
"status": "publish",
"featured": false,
"catalog_visibility": "visible",
"description": "<p>this is a test product</p>\n",
"short_description": "<p>the short desc</p>\n",
"sku": "",
"price": "9",
"regular_price": "10",
"sale_price": "9",
"date_on_sale_from": null,
"date_on_sale_from_gmt": null,
"date_on_sale_to": null,
"date_on_sale_to_gmt": null,
"price_html": "<del><span class=\"woocommerce-Price-amount amount\"><span class=\"woocommerce-Price-currencySymbol\">€</span>10.00</span></del> <ins><span class=\"woocommerce-Price-amount amount\"><span class=\"woocommerce-Price-currencySymbol\">€</span>9.00</span></ins>",
"on_sale": true,
"purchasable": true,
"total_sales": 3,
"virtual": false,
"downloadable": false,
"downloads": [],
"download_limit": -1,
"download_expiry": -1,
"external_url": "",
"button_text": "",
"tax_status": "taxable",
"tax_class": "",
"manage_stock": false,
"stock_quantity": null,
"in_stock": true,
"backorders": "no",
"backorders_allowed": false,
"backordered": false,
"sold_individually": false,
"weight": "",
"dimensions": {
"length": "",
"width": "",
"height": ""
},
"shipping_required": true,
"shipping_taxable": true,
"shipping_class": "",
"shipping_class_id": 0,
"reviews_allowed": true,
"average_rating": "0.00",
"rating_count": 0,
"related_ids": [
36,
41
],
"upsell_ids": [],
"cross_sell_ids": [],
"parent_id": 0,
"purchase_note": "",
"categories": [
{
"id": 15,
"name": "Uncategorized",
"slug": "uncategorized"
}
],
"tags": [],
"images": [
{
"id": 8,
"date_created": "2018-08-22T15:24:00",
"date_created_gmt": "2018-08-22T15:24:00",
"date_modified": "2018-08-22T15:24:00",
"date_modified_gmt": "2018-08-22T15:24:00",
"src": "http://localhost/wp-content/uploads/2018/08/sock.jpeg",
"name": "sock",
"alt": "",
"position": 0
}
],
"attributes": [
{
"id": 0,
"name": "size",
"position": 0,
"visible": true,
"variation": false,
"options": [
"small",
"medium",
"large"
]
}
],
"default_attributes": [],
"variations": [],
"grouped_products": [],
"menu_order": 0,
"meta_data": [],
"_links": {
"self": [
{
"href": "http://localhost/wp-json/wc/v2/products/7"
}
],
"collection": [
{
"href": "http://localhost/wp-json/wc/v2/products"
}
]
}
}
This works running the request using CURL on the command line, and running the request in Postman.
But now I try to create an order:
curl -X POST \
'http://localhost/wp-json/wc/v2/orders?oauth_consumer_key=ck_8cbe42fb09c04954a63994c22145270f27871dec&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1535382336&oauth_nonce=P8RWaD9DnZ9&oauth_version=1.0&oauth_signature=GFxtI7+gqbah5fu68gSdqL4V3fs=' \
-H 'Cache-Control: no-cache' \
-H 'Postman-Token: f17bec6a-c76b-4a6c-8797-1df9dac73f36' \
-d '{
"payment_method": "bacs",
"payment_method_title": "Direct Bank Transfer",
"set_paid": true,
"billing": {
"first_name": "John",
"last_name": "Doe",
"address_1": "969 Market",
"address_2": "",
"city": "San Francisco",
"state": "CA",
"postcode": "94103",
"country": "US",
"email": "john.doe#example.com",
"phone": "(555) 555-5555"
},
"shipping": {
"first_name": "John",
"last_name": "Doe",
"address_1": "969 Market",
"address_2": "",
"city": "San Francisco",
"state": "CA",
"postcode": "94103",
"country": "US"
},
"line_items": [
{
"product_id": 7,
"quantity": 1
}
],
"shipping_lines": [
{
"method_id": "flat_rate",
"method_title": "Flat Rate",
"total": 7
}
]
}'
When I run the above request with Postman, it creates an empty order and I get the response:
{
"id": 59,
"parent_id": 0,
"number": "59",
"order_key": "wc_order_5b8413f8ed585",
"created_via": "rest-api",
"version": "3.4.4",
"status": "pending",
"currency": "EUR",
"date_created": "2018-08-27T15:08:40",
"date_created_gmt": "2018-08-27T15:08:40",
"date_modified": "2018-08-27T15:08:41",
"date_modified_gmt": "2018-08-27T15:08:41",
"discount_total": "0.00",
"discount_tax": "0.00",
"shipping_total": "0.00",
"shipping_tax": "0.00",
"cart_tax": "0.00",
"total": "0.00",
"total_tax": "0.00",
"prices_include_tax": false,
"customer_id": 0,
"customer_ip_address": "",
"customer_user_agent": "",
"customer_note": "",
"billing": {
"first_name": "",
"last_name": "",
"company": "",
"address_1": "",
"address_2": "",
"city": "",
"state": "",
"postcode": "",
"country": "",
"email": "",
"phone": ""
},
"shipping": {
"first_name": "",
"last_name": "",
"company": "",
"address_1": "",
"address_2": "",
"city": "",
"state": "",
"postcode": "",
"country": ""
},
"payment_method": "",
"payment_method_title": "",
"transaction_id": "",
"date_paid": null,
"date_paid_gmt": null,
"date_completed": null,
"date_completed_gmt": null,
"cart_hash": "",
"meta_data": [],
"line_items": [],
"tax_lines": [],
"shipping_lines": [],
"fee_lines": [],
"coupon_lines": [],
"refunds": [],
"_links": {
"self": [
{
"href": "http://localhost/wp-json/wc/v2/orders/59"
}
],
"collection": [
{
"href": "http://localhost/wp-json/wc/v2/orders"
}
]
}
}
Bizarrely, when i run the EXACT same request with CURL on the command line, I get the response:
{"code":"woocommerce_rest_authentication_error","message":"Invalid signature - provided signature does not match.","data":{"status":401}}
If I run the request in Postman, but for "product_id" I use "this is a completely fake id" it still creates an empty order and responds with:
{
"id": 60,
"parent_id": 0,
"number": "60",
"order_key": "wc_order_5b84148e002da",
"created_via": "rest-api",
"version": "3.4.4",
"status": "pending",
"currency": "EUR",
"date_created": "2018-08-27T15:11:10",
"date_created_gmt": "2018-08-27T15:11:10",
"date_modified": "2018-08-27T15:11:10",
"date_modified_gmt": "2018-08-27T15:11:10",
"discount_total": "0.00",
"discount_tax": "0.00",
"shipping_total": "0.00",
"shipping_tax": "0.00",
"cart_tax": "0.00",
"total": "0.00",
"total_tax": "0.00",
"prices_include_tax": false,
"customer_id": 0,
"customer_ip_address": "",
"customer_user_agent": "",
"customer_note": "",
"billing": {
"first_name": "",
"last_name": "",
"company": "",
"address_1": "",
"address_2": "",
"city": "",
"state": "",
"postcode": "",
"country": "",
"email": "",
"phone": ""
},
"shipping": {
"first_name": "",
"last_name": "",
"company": "",
"address_1": "",
"address_2": "",
"city": "",
"state": "",
"postcode": "",
"country": ""
},
"payment_method": "",
"payment_method_title": "",
"transaction_id": "",
"date_paid": null,
"date_paid_gmt": null,
"date_completed": null,
"date_completed_gmt": null,
"cart_hash": "",
"meta_data": [],
"line_items": [],
"tax_lines": [],
"shipping_lines": [],
"fee_lines": [],
"coupon_lines": [],
"refunds": [],
"_links": {
"self": [
{
"href": "http://localhost/wp-json/wc/v2/orders/60"
}
],
"collection": [
{
"href": "http://localhost/wp-json/wc/v2/orders"
}
]
}
}
So I have a number of problems:
1/ No matter what product_id I use, the order that's created is always empty
2/ Even if I provide a fake product_id, an order is still created by Woo Commerce. Surely this is incorrect?
3/ Running the create order requests on the command line is giving different responses to Postman
I was missing:
-H 'Content-Type: application/json' \
from the request. I had this entered in Postman, but had not ticked the check box: