Successfully reported this slideshow.
Your SlideShare is downloading. ×

Caldera Learn - LoopConf WP API + Angular FTW Workshop

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad

Check these out next

1 of 122 Ad

Caldera Learn - LoopConf WP API + Angular FTW Workshop

Download to read offline

The WordPress REST API combined with a JavaScript MVC framework opens up new possibilities for better, more intuitive interfaces that solve scaling and other challenges. In this workshop we will use AngularJS, the best — because we said so — and the WordPress REST API to create custom applications. First we will walk through creating custom API routes and endpoints to interact with your data, followed by two unique ways to use AngularJS to create better interfaces for your projects. This will include a decoupled front-end and single page web app as a plugin interface.

The WordPress REST API combined with a JavaScript MVC framework opens up new possibilities for better, more intuitive interfaces that solve scaling and other challenges. In this workshop we will use AngularJS, the best — because we said so — and the WordPress REST API to create custom applications. First we will walk through creating custom API routes and endpoints to interact with your data, followed by two unique ways to use AngularJS to create better interfaces for your projects. This will include a decoupled front-end and single page web app as a plugin interface.

Advertisement
Advertisement

More Related Content

Slideshows for you (20)

Similar to Caldera Learn - LoopConf WP API + Angular FTW Workshop (20)

Advertisement

Recently uploaded (20)

Caldera Learn - LoopConf WP API + Angular FTW Workshop

  1. 1. WORDPRESS REST API AND ANGULARJS FTW! Roy Sivan and Josh Pollock CalderaLearn.com
  2. 2. CalderaLabs.org Join our Slack LoopConf Slack: #workshop-wpapi We will be posting links, taking questions, and communicating throughout the day via Slack
  3. 3. CalderaLabs.org Hi I'm Josh Lead Developer: Caldera Labs I make WordPress plugins I teach about WordPress I wrote a book about the WordPress REST API I am a core contributor to WordPress I am a member of The WP Crowd
  4. 4. CalderaLearn.com Hi, I'm Roy Senior Software Engineer: The Walt Disney Company I am a member of The WP Crowd I blog on roysivan.com I teach on Lynda.com & CalderaLearn People say Hi to me a lot
  5. 5. What We're Covering Today REST API 101 Building Custom REST APIs Unit Testing Custom REST APIs LUNCH BREAK AngularJS (1.x) Basics Building Decoupled Front-ends Building Plugin Admin Screens
  6. 6. Educational Philosophy All code is or is based on real world projects We will show different ways of doing the same thing. Please ask why it's different ◇ we may give you a good answer Stop us and ask questions
  7. 7. Structure For Today foreach ( $sections as $section ) : Concepts / Lecture Example Code Walkthrough (you will be cloning locally) Hands-on DIY Group Walk Through Code Group endforeach;
  8. 8. WARNING This workshop is to help you understand the basics and some advanced technologies. Nothing will be production ready code.
  9. 9. What you need for today IDE or text editor (PHPStorm, Sublime, etc.) Local WP install (VVV, DesktopServer, etc.) npm PHPUnit (optional, included in VVV) Composer (optional) AngularJS Batarang Chrome Extension https://chrome.google.com/webstore/detail/angularjs-batarang/ighdmehid hipcmcojjgiloacoafjmpfk?hl=en
  10. 10. CalderaLabs.org 0. WordPress REST API? Do we even need it?
  11. 11. CalderaLabs.org NO But you can build cool stuff with it.
  12. 12. It’s all about the Data Data! Data! Data!
  13. 13. “ CalderaLearn.com The API allows you to take WP data and put it in a bucket. What you do with that bucket is up to you. -- Morten Rand Hendriksen @mor10
  14. 14. CalderaLabs.org What can we build with it? Facebooks? Myspaces?
  15. 15. API Powered Stuff Things you can build that are powered by the API Phone apps Custom UI widgets on your site Why use feed of another site, when you can API Custom Dashboards in the admin Custom WP Dashboard (YAS!) User role based dashboard
  16. 16. CalderaLabs.org 1. Custom REST APIs With The WordPress REST API
  17. 17. CalderaLabs.org Modify Default Routes
  18. 18. CalderaLabs.org WordPress Gives Us Routes Which We Can Extend
  19. 19. CalderaLabs.org Create Your Own Routes
  20. 20. CalderaLabs.org Or We Can Make Our Own With The Same Tools
  21. 21. How The REST API Works WP_REST_Server ??WP_REST_Request WP_REST_Response
  22. 22. How The REST API Works WP_REST_Server Route Callback WP_REST_Request WP_REST_Response ERROR! ERROR!
  23. 23. CalderaLabs.org Let's Talk About The Orange Box The Route Callback
  24. 24. Customizing The Defaults
  25. 25. CalderaLabs.org Extending Defaults Adding Post Type Support
  26. 26. add_action( 'init', 'my_book_cpt' ); function my_book_cpt() { $labels = array(...); $args = array( ... 'rest_controller_class' => 'WP_REST_Posts_Controller', 'show_in_rest' => true, 'rest_base' => 'books-api', ); register_post_type( 'book', $args ); } Add REST API Support To Post Type Registration
  27. 27. add_action( 'init', 'my_custom_post_type_rest_support', 25 ); function my_custom_post_type_rest_support() { global $wp_post_types; $post_type_name = 'book'; if( isset( $wp_post_types[ $post_type_name ] ) ) { $wp_post_types[$post_type_name]->show_in_rest = true; $wp_post_types[$post_type_name]->rest_base = $post_type_name; $wp_post_types[$post_type_name]->rest_controller_class = 'WP_REST_Posts_Controller'; } } Add REST API Support To An Existing Post Type
  28. 28. CalderaLabs.org Extending Defaults Adding A Custom Field
  29. 29. CalderaLabs.org Custom Field Is Any Data Not just meta fields
  30. 30. function slug_get_meta_field( $object, $field_name, $request ) { return get_post_meta( $object[ 'id' ], $field_name ); } function slug_update_meta( $value, $object, $field_name ) { if ( ! $value || ! is_string( $value ) ) { return; } return update_post_meta( $object->ID, $field_name, strip_tags( $value ) ); } Adding Custom Fields To A Response: Callbacks
  31. 31. add_action( 'rest_api_init', 'slug_register_spaceship' ); function slug_register_spaceship() { register_api_field( 'post', 'starship', array( 'get_callback' => 'slug_get_meta_field', 'update_callback' => 'slug_update_meta_field', ) ); } Adding Custom Fields To A Response: Registration
  32. 32. CalderaLabs.org Making Custom REST APIs The Basics How It Works
  33. 33. add_action( 'rest_api_init', function () { register_rest_route( 'myplugin/v1', '/author/(?P<id>d+)', array( 'methods' => 'GET', 'callback' => 'route_callback', ) ); } ); Registering A Route
  34. 34. CalderaLabs.org Creating Callback Class Register Route(s)
  35. 35. class my_simple_route { public function register_routes(){ $namespace = 'my-api/v1'; register_rest_route( $namespace, '/items', [ 'methods' => 'GET', 'callback' => [ $this, 'get_items' ], 'permissions_callback' => [ $this, 'get_items_permissions' ] ] ); ... } } Registering Route(s)
  36. 36. class my_simple_route { public function register_routes(){ ... register_rest_route( $namespace, '/items/(?P<id>d+)', [ 'methods' => 'GET', 'callback' => [ $this, 'get_item' ], 'permissions_callback' => [ $this, 'get_item_permissions' ] ] ); } } Registering Route(s)
  37. 37. CalderaLabs.org Creating Callback Class Defining Route Fields
  38. 38. register_rest_route( $this->namespace, '/items/(?P<id>d+)', [ ... 'args' => [ 'type' => [ 'required' => true, 'validate_callback' => [ $this, 'validate_type' ] ], 'number' => [ 'default' => 5, 'sanitize_callback' => 'absint' ] ] ] ); Registering Route(s) : Defining Fields
  39. 39. Registering Route(s) : Field Sanitization Callback Use to ensure data is safe. Defined using a callable. Change data to a safe value. Return prepared value
  40. 40. Registering Route(s) : Field Validation Callback Use to ensure data is correct. Defined using a callable. Used to reject invalid requests Return true or false
  41. 41. Registering Route(s) : Field Validation Callback public function validate_type( $value ){ if( !in_array( $value, [ 'big', 'small', 'very-small' ] ) ){ return false; } return true; }
  42. 42. CalderaLabs.org Creating Callback Class Permissions Callback
  43. 43. Registering Route(s) : Permissions Callback Determine if user is authorized. Return true or false
  44. 44. Registering Route(s) : Permissions Callback Example: Make require login. public function get_items_permissions(){ if( is_user_logged_in() ){ return true; } return false; }
  45. 45. Registering Route(s) : Permissions Callback Example: Limit To Admins public function get_items_permissions(){ if( current_user_can( 'manage_options' ) ){ return true; } return false; }
  46. 46. Registering Route(s) : Permissions Callback Example: Allow Always public function get_items_permissions(){ return true; }
  47. 47. CalderaLabs.org Creating Callback Class Route Callback Function
  48. 48. Registering Route(s) : Callback Do something with the request Gets an object of WP_REST_Request Should return WP_REST_Response or WP_Error
  49. 49. WP_Rest_Response Represents current request Contains: ◇ Parameters ◇ Headers No need to access $_GET, $_POST, $_REQUEST Implements Arrayaccess $request->param( 'field_name' ); $request[ 'field_name' ];
  50. 50. Registering Route(s) : Callback public function get_item( WP_REST_Request $request ){ $id = $request[ 'id' ]; $item = slug_crud_get( $id ); if( ! empty( $item ) && ! is_wp_error( $item ) ){ return rest_ensure_response( $item ); }elseif ( is_wp_error( $item ) ){ return $item; }else{ $response = new WP_REST_Response( 'No items found', 404 ); return $response; } }
  51. 51. CalderaLabs.org Initializing The Class The rest_api_init action
  52. 52. CalderaLabs.org rest_api_init Use To Load Code Only Needed In REST API Requests
  53. 53. Registering Route(s) : Initializing add_action( 'rest_api_init', function(){ $route = new my_simple_route(); $route->register_routes(); });
  54. 54. CalderaLabs.org 2. Unit Testing Custom REST APIs Subtitle
  55. 55. CalderaLearn.com Goal Only Test Our Endpoints
  56. 56. CalderaLabs.org PHPUnit + WP_UnitTestCase
  57. 57. CalderaLabs.org Setting Up Prepare To Test
  58. 58. Unit Testing 101 Make Sure Things Return What They Should Return $this->assertEquals( 42, function_that_returns_42() ); $this->assertSame( '42', function_that_returns_42() ); $this->assertArrayHasKey( 'roy', array( 'roy' => 'hi' ) ); Google: "Pippin Williamson Unit Tests for WordPress Plugins"
  59. 59. Install PHPUnit wget https://phar.phpunit.de/phpunit.phar chmod +x phpunit.phar mv phpunit.phar /usr/local/bin/phpunit wget https://phar.phpunit.de/phpunit.phar php phpunit.phar
  60. 60. Install WP CLI curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/ phar/wp-cli.phar php wp-cli.phar --info chmod +x wp-cli.phar sudo mv wp-cli.phar /usr/local/bin/wp
  61. 61. Initialize Unit Tests wp scaffold plugin-tests
  62. 62. CalderaLabs.org Writing Tests Testing!
  63. 63. CalderaLearn.com Mock Requests
  64. 64. An Example public function test_get_items_author_query() { $this->factory->post->create( array( 'post_author' => 4 ) ); $this->factory->post->create( array( 'post_author' => 4 ) ); $this->factory->post->create( array( 'post_author' => 2 ); $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); $response = $this->server->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); $this->assertEquals( 3, count( $response->get_data() ) ); }
  65. 65. An Example public function test_get_items_author_query() { $this->factory->post->create( array( 'post_author' => 4 ) ); $this->factory->post->create( array( 'post_author' => 4 ) ); $this->factory->post->create( array( 'post_author' => 2 ); $request = new WP_REST_Request( 'GET', '/wp/v2/posts' ); $request->set_param( 'author', 4 ); $response = $this->server->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); $this->assertEquals( 2, count( $response->get_data() ) ); }
  66. 66. CalderaLearn.com Test Case
  67. 67. Unit Test Case For REST API Endpoints Create a reusable class that: Create Instance of WP_REST_Server Boot routes Make sure route is booted
  68. 68. Test Case Outline class Test_API extends WP_UnitTestCase { /** @var WP_REST_Server*/ protected $server; protected $namespaced_route = 'caldera-forms/v1'; public function setUp() {} public function test_register_route() {} public function test_endpoints() {} }
  69. 69. Test Case: Set Up protected $server; public function setUp() { parent::setUp(); /** @var WP_REST_Server $wp_rest_server */ global $wp_rest_server; $this->server = $wp_rest_server = new WP_REST_Server; do_action( 'rest_api_init' ); }
  70. 70. Test Case: Test Route Is Registered public function test_register_route() { $routes = $this->server->get_routes(); $this->assertArrayHasKey( $this->namespaced_route, $routes ); }
  71. 71. Test Case: Test Endpoints Exist public function test_endpoints() { $the_route = $this->namespaced_route; $routes = $this->server->get_routes(); foreach( $routes as $route => $route_config ) { if( 0 === strpos( $the_route, $route ) ) { $this->assertTrue( is_array( $route_config ) ); foreach( $route_config as $i => $endpoint ) { $this->assertArrayHasKey( 'callback', $endpoint ); $this->assertArrayHasKey( 0, $endpoint[ 'callback' ], get_class( $this ) ); $this->assertArrayHasKey( 1, $endpoint[ 'callback' ], get_class( $this ) ); $this->assertTrue( is_callable( array( $endpoint[ 'callback' ][0], $endpoint[ 'callbac ][1] ) ) ); } } }
  72. 72. CalderaLearn.com Testing Custom Routes
  73. 73. Test Your API Only!!! Test internal logic elsewhere Test control of that logic Test response format Trust core
  74. 74. Example Route Test class Test_Hi extends Test_API { protected $namespace = '/hi-api/v1/names'; public function test_list() { $request = new WP_REST_Request( 'GET', $this->namespace ); $response = $this->server->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); $data = $response->get_data(); $this->assertArrayHasKey( 'name', $data[0] ); $this->assertEquals( 'shawn', $data[0][ 'name' ] ); $this->assertArrayHasKey( 'name', $data[1] ); $this->assertEquals( 'roy', $data[1][ 'name' ] ); } }
  75. 75. Example Route Test class Test_Hi extends Test_API { protected $namespace = '/hi-api/v1/names'; public function test_single(){ $request = new WP_REST_Request( 'GET', $this->namespace . '/roy' ); $response = $this->server->dispatch( $request ); $this->assertEquals( 200, $response->get_status() ); $data = $response->get_data(); $this->assertArrayHasKey( 'name', $data ); $this->assertEquals( 'roy', $data[ 'name' ] ); } }
  76. 76. CalderaLabs.org 3. Getting Started With AngularJS 1.x Controllers, Templates, Services & Factories
  77. 77. CalderaLabs.org 3 con’t Why Angular? You know it’s in the title of the workshop, right?
  78. 78. “ CalderaLearn.com AngularJS is the best JavaScript Framework. -- Roy Sivan Senior WordPress Engineer at Disney (so you know he is legit)
  79. 79. Not WordPress To get you through the basics of AngularJS we will be using sample data, not WP Our First Project No npm or gulp We aren’t using any build tools, this is a pure sample https://github.com/caldera-learn/angularjs-intro
  80. 80. Quicker & Easier Quicker to get going to show overall concepts. Honest Truth We already have a few projects built in it that are ready to go Why 1.x? 2 is in RC! Roy is lazy I am not lazy, but didn’t have time to learn it deeply enough yet to give a full workshop on it. Josh Switched Teams He used to be team NG1. Now he is team VueJS.
  81. 81. HTML powered JavaScript With Angular 1.x you can use HTML to do most things reserved for PHP. Get the data using JS, template with HTML. No PHP needed.
  82. 82. CalderaLearn.com Step 1 Setup the APP
  83. 83. Setting up the app All functionality lives within 1 app We use ng-app to encapsulate the app in the DOM, it can be used on any element including HTML. Using it on the HTML encapsulates the whole DOM
  84. 84. Sample Data JSON data.json This file is going to be a sample of data, that we are going to use to build out a simple Angular App, we will then replace it with the WordPress REST API
  85. 85. Injectables Injectables are objects which can be injected and used in controllers, directives, etc. We will be creating our own later...
  86. 86. AngularJS Controllers Controller is all about $scope A Controller is defined by a JavaScript constructor function that is used to augment the AngularJS Scope. When a Controller is attached to the DOM via the ng-controller directive, AngularJS will instantiate a new Controller object, using the specified Controller's constructor function All new data to be used must be stored within $scope.your_key ng-controller
  87. 87. All Together - JS wpNG = {}; wpNG.app = ( function( app ){ console.log( 'initializing..' ); // define our app app = angular.module('wpAngularApp', []) .controller( 'listView', ['$scope', '$http', function( $scope, $http ) { … }]); return app; }(wpNG.app || {});
  88. 88. All Together - HTML <div ng-app="wpAngularApp" id="app-container"> <div ng-controller="listView"> <!-- List View --> </div> </div>
  89. 89. CalderaLearn.com Step 2 HTML! Looping through the data
  90. 90. First we must get data $http is a jQuery AJAX wrapper & returns a promise $http({method: ‘’, url: ‘’}) $http.get(url) $http.post(url) Because it returns a promise we use .then()
  91. 91. Say hello to JSON JSON is the JavaScript Object Notation. We display JSON with curly brackets We work with JSON similar to PHP arrays {{Object.key}} displays that key value In PHP that would be $object[‘key’];
  92. 92. The loop HTML <div ng-controller="listView"> <h2>Posts</h2> <!-- Loop through $scope.posts --> <article ng-repeat="post in posts"> <h2>{{post.title}}</h2> </article> </div>
  93. 93. Add more data <article ng-repeat="post in posts"> <img ng-src="{{post.image}}" /> <h2>{{post.title}}</h2> <div class="post-content" ng-bind-html="post.content | to_trusted"> </div> </article>
  94. 94. CalderaLabs.org 4. Decoupled Front-ends With AngularJS A site has no WordPress
  95. 95. Decoupled Front End Decouple front end is a front end app that doesn’t live within WordPress at all. Your website: myawesomesite.com Decoupled: myawesomeapp.com - but running on the same data! How cool is that!
  96. 96. Decoupled Use Cases Phone App Piece of functionality that communicates with WP data Advanced, Unique UI Combine with other APIs Different Stack (decoupled app doesn’t need to be PHP)
  97. 97. Our decoupled App npm gulp REST url that is publicly accessible (we will give you one) Tacos, you always need tacos. What you will need: https://github.com/caldera-learn/decoupled-app
  98. 98. CalderaLabs.org What About CORS? Cross-Origin Resource Sharing
  99. 99. CORS Allows browsers to make requests across domains. IS NOT SECURITY!! By default WordPress REST API is same-origin only. Set at rest_pre_serve_request hook
  100. 100. Allow All Domains For All Methods add_action( 'rest_api_init', function() { remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' ); add_filter( 'rest_pre_serve_request', function( $value ) { header( 'Access-Control-Allow-Origin: *' ); header( 'Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE' ); header( 'Access-Control-Allow-Credentials: true' ); return $value; }); }, 15 );
  101. 101. Only Allow GET Requests add_action( 'rest_api_init', function() { remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' ); add_filter( 'rest_pre_serve_request', function( $value ) { $origin = get_http_origin(); if ( $origin ) { header( 'Access-Control-Allow-Origin: ' . esc_url_raw( $origin ) ); } header( 'Access-Control-Allow-Origin: ' . esc_url_raw( site_url() ) ); header( 'Access-Control-Allow-Methods: GET' ); return $value; }); }, 15 );
  102. 102. Allows From Certain Origins add_action( 'rest_api_init', function() { remove_filter( 'rest_pre_serve_request', 'rest_send_cors_headers' ); add_filter( 'rest_pre_serve_request', function( $value ) { $origin = get_http_origin(); if ( $origin && in_array( $origin, array( 'https://hiroy.club' ) ) ) { header( 'Access-Control-Allow-Origin: ' . esc_url_raw( $origin ) ); header( 'Access-Control-Allow-Methods: POST, GET, OPTIONS, PUT, DELETE' ); header( 'Access-Control-Allow-Credentials: true' ); } return $value; }); }, 15 );
  103. 103. CalderaLabs.org Factories, Storage, UI-Router, and Templates All the things!
  104. 104. AngularJS Factories Factories allow you to create injectable objects. Inject $resource into the factory it to create an object that can automatically handle REST calls. app.factory('Posts',function($resource){ return $resource( ngWP.config.api + ‘/:post_type/:ID?per_page=:per_page’, { post_type: 'posts', ID:'@id', per_page: ngWP.config.posts_per_page, }); })
  105. 105. AngularJS Factories cont’d Posts.get() Posts.query() - like get, but expects array Posts.save() Posts.delete() Posts.remove() Unlike $http these do not return promises
  106. 106. Local Storage https://github.com/grevory/angular-local-storage An AngularJS module that gives you access to the browser’s local storage with cookie fallback. Local storage is a key/value store which we call on. In our example the regular posts (blog) will check for local storage first, before hitting the API.
  107. 107. UI-Router https://github.com/angular-ui/ui-router The Angular-UI project adds modules and functionality to Angular (think WP plugins for WP) State Driven Routing & a lot of other functionality we won’t need. Shows its true power when you have 1 view within another (post.detail within post.list)
  108. 108. Templates Separating out templates into templates directory Cleaner code Separation of views 1 controller can have 1 template 1 template can be powered by multiple controllers Pure HTML (with JSON)
  109. 109. CalderaLabs.org Now Entering The Danger Zone
  110. 110. First thing is first Clone the repo You will need to run npm install and gulp
  111. 111. The config file Create a file called config.js in /assets/js var ngWP = ngWP || {}; ngWP.config = { api: 'https://calderaforms.com/wp-json/', posts_per_page: 5 // Not needed for our menu: 'app' };
  112. 112. Stepping through the code Main APP file UI Router Definitions Small controllers live here Templates Blog/Author List Blog Detail Product List
  113. 113. CalderaLabs.org 5. Plugin Admin Screens With AngularJS The UI Router Making Mini-Apps To Make WordPress Better
  114. 114. Very Similar To Regular NG App
  115. 115. Basic Use add_admin_menu() to print basic HTML Use ui-router to switch routes inside admin page. Use wp_localize_script() for config
  116. 116. Questions? Use PHP or HTML files for templates? How to handle translations? wp_localize_script() PHP templates
  117. 117. Let's Look At Some Code!
  118. 118. What’s Next? Angular 2 Theme currently in development github.com/royboy789/Angular-Wordpress-Theme/tree/v7 Vue.JS - The new simple JS framework ReactJS - What everyone else wants you to learn Admin Theme Boilerplate github.com/WordPress-Admin-JavaScript-Boilerplate/ReactJS-Boilerplate
  119. 119. Want more Roy & Josh teachings? Caldera Learn teaches through 4-week live classroom style webinars. Teachers are Josh and/or Roy with future guest teachers
  120. 120. CalderaLabs.org Josh Pollock JoshPress.net CalderaForms.com CalderaLearn.com CalderaLabs.org @Josh412
  121. 121. CalderaLabs.org Roy Sivan theWPCrowd.com RoySivan.com Lynda.com CaldraLearn.com @royboy789
  122. 122. CalderaLabs.org Want To Learn More? Coming Very Soon CalderaLearn.com

×