{"id":1191,"date":"2020-01-13T00:10:01","date_gmt":"2020-01-12T17:10:01","guid":{"rendered":"https:\/\/lab.wptips.dev\/?p=1191"},"modified":"2020-06-21T09:45:39","modified_gmt":"2020-06-21T02:45:39","slug":"gutenberg-table-as-simple-db","status":"publish","type":"post","link":"https:\/\/pixelstudio.id\/blog\/gutenberg-table-as-simple-db\/","title":{"rendered":"How to Parse Gutenberg Table into JSON"},"content":{"rendered":"\n<blockquote class=\"wp-block-quote\"><p>Gutenberg (also knows as Block Editor) is the official drag-n-drop builder in WordPress version 5.0+.<\/p><\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">Case Study<\/h2>\n\n\n\n<p>Few months ago, I had a request from a music school to make a <strong>Price Calculator.<\/strong> We choose a course and duration, then it will show the price.<\/p>\n\n\n\n<p>Their brochure has a pricing table like below.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Months<\/th><th>Guitar<\/th><th>Acoustic Guitar<\/th><th>Electric Guitar<\/th><th>Bass<\/th><\/tr><\/thead><tbody><tr><td>1<\/td><td>150<\/td><td>170<\/td><td>160<\/td><td>155<\/td><\/tr><tr><td>2<\/td><td>290<\/td><td>335<\/td><td>310<\/td><td>300<\/td><\/tr><tr><td>3<\/td><td>425<\/td><td>490<\/td><td>450<\/td><td>440<\/td><\/tr><tr><td>&#8230;<\/td><td>&#8230;<\/td><td>&#8230;<\/td><td>&#8230;<\/td><td>&#8230;<\/td><\/tr><tr><td>12<\/td><td>1,600<\/td><td>1,850<\/td><td>1,780<\/td><td>1,670<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>The increase and discount is not linear, so we can&#8217;t use formula to calculate.<\/p>\n\n\n\n<p>The client said they will edit it often. Plus, there are many categories like Piano and Violin. So we need to make it as friendly to edit as possible.<\/p>\n\n\n\n<p>After going through many options, we decided to use a plain-old table because they can <strong>copy-paste it from Excel<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1 &#8211; Create a Page with the Data Table<\/h2>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"750\" height=\"371\" src=\"https:\/\/pixelstudio.id\/blog\/wp-content\/uploads\/2020\/01\/simple-db-create-page.jpg\" alt=\"\" class=\"wp-image-1199\" srcset=\"https:\/\/pixelstudio.id\/blog\/wp-content\/uploads\/2020\/01\/simple-db-create-page.jpg 750w, https:\/\/pixelstudio.id\/blog\/wp-content\/uploads\/2020\/01\/simple-db-create-page-480x237.jpg 480w\" sizes=\"auto, (max-width: 750px) 100vw, 750px\" \/><figcaption>Page containing the data table<\/figcaption><\/figure>\n\n\n\n<p>I use hashtag format for the page title to let the client know that they shouldn&#8217;t edit that. I also put it as a child page of Price Calculator just to make it tidier.<\/p>\n\n\n\n<p>If you use an SEO plugin, set this page to noindex.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2 &#8211; Create Pseudocode<\/h2>\n\n\n\n<p>Let&#8217;s plan the logic and flow:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">Call a function - pass in: Page name, Course name, Duration\n\nGet the page with that Page name.\n\nGet the page's content\n\nUse regex to find the Column using Course name (eg: \"Acoustic Guitar\" at column 3)\n\nUse regex to find the Row using Duration\n\nReturn the number that is in the found Column and Row<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3 &#8211; Coding<\/h2>\n\n\n\n<pre title=\"functions.php\" class=\"wp-block-code\"><code lang=\"php\" class=\"language-php line-numbers\">get_price( '#guitar', 'Electric Guitar', 2 );\nfunction get_price( $page_name, $course, $duration ) {\n  $page = get_page_by_title( $page_name );\n  $content = $page->post_content;\n\n  \/\/ find columns\n  preg_match_all('\/&lt;th>(.*)&lt;\\\/th>\/Ui', $content, $columns );\n  $column_index = array_search( $course, $columns[1] );\n\n  \/\/ find row\n  preg_match( \"\/&lt;tr>&lt;td>($duration.+)&lt;\\\/td>&lt;\\\/tr>\/Ui\", $content, $row_html );\n  $row = preg_split( '\/&lt;\\\/td>&lt;td>\/', $row_html[1] );\n  \n  \/\/ get price\n  return $row[ $column_index ];\n}<\/code><\/pre>\n\n\n\n<p><strong>LINE 9<\/strong> &#8211; It matches all header texts. Then we use <code>array_search<\/code> to get the index.<\/p>\n\n\n\n<p><strong>LINE 13<\/strong> &#8211; It looks for first column that contain the specified duration. Then it returns the HTML code of that row like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"html\" class=\"language-html\">\n3&lt;\/td>&lt;td>425&lt;\/td>&lt;td>490&lt;\/td>&lt;td>450&lt;\/td>&lt;td>440\n<\/code><\/pre>\n\n\n\n<p><strong>LINE 14<\/strong> &#8211; We split the HTML above to get all row texts.<\/p>\n\n\n\n<p><strong>LINE 17<\/strong> &#8211; Now we can get the price by matching the row and column.<\/p>\n\n\n\n<hr class=\"wp-block-separator is-style-dots\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Where else can you apply this? If you have a simple ACF Repeater, you can replace it with this technique.<\/p>\n\n\n\n<p>Other than being easier to edit, I believe the performance is better too since we only do 1 query and no heavy task.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote\"><p>Leave a comment below if you have any question or want to share your story of using this tip! \ud83d\ude42<\/p><\/blockquote>\n","protected":false},"excerpt":{"rendered":"<p>If you have a simple ACF Repeater Field, try replacing it with a plain Table. Other than being easier to edit, the performance is much better.<\/p>\n","protected":false},"author":1,"featured_media":1203,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[28,22,31,41],"class_list":["post-1191","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-gutenberg","tag-database","tag-gutenberg","tag-php","tag-regex"],"blocksy_meta":{"page_structure_type":"default","page_enable_vertical_spacing":"yes","has_hero_section":"default","hero_section":"type-1","hero_alignment1":"left","hero_alignment2":"center","hero_height":"230px","page_title_bg_type":"color","48a95f810c0c60aa618904fc02ac0202":"","custom_hero_background":{"attachment_id":null},"parallax":{"desktop":false,"tablet":false,"mobile":false},"409b3a5fed5b24bce6fcf7fa73539607":"","single_meta_elements":{"author":true,"date":true,"categories":true,"comments":true,"updated":false,"tags":false},"has_meta_label":"yes","date_format_source":"custom","single_meta_date_format":"M j, Y","cf4bcc0fd5f854d4a42ff321b89c2f20":"","page_excerpt_visibility":{"desktop":true,"tablet":true,"mobile":false},"pageTitleFont":{"family":"Default","variation":"Default","size":{"desktop":"32px","tablet":"30px","mobile":"25px"},"line-height":"1.3","letter-spacing":"CT_CSS_SKIP_RULE","text-transform":"CT_CSS_SKIP_RULE","text-decoration":"CT_CSS_SKIP_RULE"},"pageTitleFontColor":{"default":{"color":"var(--paletteColor4)"}},"pageMetaFont":{"family":"Default","variation":"n6","size":{"desktop":"12px","tablet":"12px","mobile":"12px"},"line-height":"1.3","letter-spacing":"CT_CSS_SKIP_RULE","text-transform":"uppercase","text-decoration":"CT_CSS_SKIP_RULE"},"pageMetaFontColor":{"default":{"color":"CT_CSS_SKIP_RULEDEFAULT"},"hover":{"color":"CT_CSS_SKIP_RULEDEFAULT"}},"pageExcerptFont":{"family":"Default","variation":"n5","size":"CT_CSS_SKIP_RULE","line-height":"CT_CSS_SKIP_RULE.65","letter-spacing":"CT_CSS_SKIP_RULE","text-transform":"CT_CSS_SKIP_RULE","text-decoration":"CT_CSS_SKIP_RULE"},"pageExcerptColor":{"default":{"color":"CT_CSS_SKIP_RULEDEFAULT"}},"pageTitleOverlay":{"default":{"color":"rgba(41, 51, 60, 0.2)"}},"pageTitleBackground":{"default":{"color":"#EDEFF2"}},"1533fc73705c6c8de6448826164f8258":"","542b59355d6851298de6291c7487bc5e":"","81758ae5a3e074bccb0acebb3dcc4e09":""},"_links":{"self":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts\/1191","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/comments?post=1191"}],"version-history":[{"count":9,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts\/1191\/revisions"}],"predecessor-version":[{"id":1651,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/posts\/1191\/revisions\/1651"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/media\/1203"}],"wp:attachment":[{"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/media?parent=1191"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/categories?post=1191"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pixelstudio.id\/blog\/wp-json\/wp\/v2\/tags?post=1191"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}